<commit_before>function d3graph (div, width, height, drawNode, drawEdge) {
  let svg = div.append('svg')
  .attr('width', width).attr('height', height);

  let nodes = {};
  let edges = {};
  let history = [];

  function addNode(id, data) {
    if (nodes[id] !== undefined) throw 'Node ' + id + ' already exists';
    nodes[id] = data;
  }
  function getNode(id) {
    let node = nodes[id];
    if (node == undefined) throw 'Node ' + id + ' does not exist';
    return node;
  }
  function delNode(nodeId) {
    nodes[nodeId] = undefined;
  }
  function addEdge(edgeId, srcId, dstId, data) {
    edges[edgeId] = {id: edgeId, srcId: srcId, dstId: dstId, data: data};
  }
  function delEdge(edgeId) {
    edges[edgeId] = undefined;
  }

  function buildGraph() {
    let graph = {};
    for (let id in nodes) {
      if (nodes[id] !== undefined) {
        graph[id] = {src:[], dst: []};
      }
    }
    for (let eid in edges) {
      let edge = edges[eid];
      if (graph[edge.srcId] !== undefined && graph[edge.dstId] != undefined) {
        graph[edge.srcId].dst.push(edge);
        graph[edge.dstId].src.push(edge);
      }
    }
    return graph;
  }

  function buildLayers(graph) {
    let queue = [];
    // first layer has no src
    for (let id in graph) {
      if (graph[id].src.length === 0) {
        queue.push(id);
      }
    }
    queue.push(undefined);

    let level = 0;
    while (queue.length > 0) {
      let id = queue.shift();
      if (id === undefined) {
        level++;
        if (queue.length === 0) break;
        queue.push(undefined);
        continue;
      }

      graph[id].level = graph[id].level || level;
      graph[id].dst.forEach(function (edge) {
        queue.push(edge.dstId);
      });
    }
    let layers = [];
    for (let i = 0; i < level; ++i) {
      layers.push([]);
    }
    for (let id in graph) {
      layers[graph[id].level].push(id);
    }
    return layers;
  }

  function sortLayers(graph, layers) {
    for (let round = 0; round < layers.length; ++round) {
      layers.forEach(function (layer) {
        layer.forEach(function (id, index) {
          graph[id].y = (index + 1) / layer.length + 1;
        });
      });

      layers.forEach(function (layer) {
        layer.forEach(function (id) {
          let node = graph[id];
          let sum = 0.0;
          node.src.forEach(function (edge) {
            sum += graph[edge.dstId].y;
          });
          node.dst.forEach(function (edge) {
            sum += graph[edge.srcId].y;
          });
          graph[id].y = sum / (node.src.length + node.dst.length);
        });
        layer = layer.sort(function (id1, id2) {
          return graph[id1].y - graph[id2].y;
        });
      });
    }
  }

  function layoutNodes(graph, layers) {
    layers.forEach(function (layer, i) {
      let x = width * (i + 1) / (layers.length + 1);
      layer.forEach(function (id, j) {
        let y = height * (j + 1) / (layer.length + 1);
        graph[id].x = x;
        graph[id].y = y;
      });
    });
  }

  function layoutEdges(graph, layers) {
    let edgeAnchors = {};
    for (let srcId in graph) {
      let srcNode = graph[srcId];
      let edgeGroups = {};
      srcNode.dst.forEach(function (edge) {
        if (edgeGroups[edge.dstId] === undefined) {
          edgeGroups[edge.dstId] = [];
        }
        edgeGroups[edge.dstId].push(edge);
      });

      let srcMargin = 0.5 * height / (layers[srcNode.level].length + 1);

      for (let dstId in edgeGroups) {
        let edges = edgeGroups[dstId];
        let dstNode = graph[dstId];
        let dstMargin = 0.5 * height / (layers[dstNode.level].length + 1);
        let top = (srcNode.y - srcMargin + dstNode.y - dstMargin) / 2;
        let bottom = (srcNode.y + srcMargin + dstNode.y + dstMargin) / 2;
        let step = (bottom - top) / (edges.length + 1);
        let x = (srcNode.x + dstNode.x) / 2;
        edges.forEach(function (edge, i) {
          let y = (i+1) * step + top;
          edgeAnchors[edge.id] = {x: x, y: y};
        });
      }
    }
    return edgeAnchors;
  }

  function redraw(duration, ease) {
    svg.selectAll('*').remove();

    let graph = buildGraph();
    let layers = buildLayers(graph);
    sortLayers(graph, layers);
    layoutNodes(graph, layers);
    let anchors = layoutEdges(graph, layers);

    if (duration !== undefined && history.length > 0) {
      ease = ease || 'cos';
      let prevGraph = history.shift();
      let prevAnchors = history.shift();
      renderAnimation(prevGraph, graph, prevAnchors, anchors, duration, ease);
    } else {
      render(graph, anchors);
    }

    let saveGraph = {};
    for (let id in graph) {
      if (graph[id].dummy === undefined) {
        saveGraph[id] = graph[id];
      }
    }
    history.push(saveGraph);
    history.push(anchors);
  }

  function buildDummyNodes(graph1, graph2) {
    for (let id in graph2) {
      if (graph1[id] !== undefined) continue;
      let node = graph2[id];
      // fill graph1[id] with nearest parent
      let queue = [id];
      while (queue.length > 0) {
        let pid = queue.shift();
        let parent = graph1[pid];
        if (parent !== undefined) {
          graph1[id] = {x: parent.x, y: parent.y, dummy: true};
          break;
        }
        graph2[pid].src.forEach(function (edge) {
          queue.push(edge.srcId);
        });
      }
    }
  }

  function render(graph, anchors) {
    for (let id in edges) {
      let edge = edges[id];
      let srcNode = graph[edge.srcId];
      let dstNode = graph[edge.dstId];
      let anchor = anchors[id];
      if (anchor !== undefined) {
        renderEdge([srcNode, anchor, dstNode], edge.data);
      }
    }

    for (let id in graph) {
      renderNode(graph[id], nodes[id]);
    }
  }

  function renderAnimation(prevGraph, graph, prevAnchors, anchors, duration, ease) {
    buildDummyNodes(prevGraph, graph);
    buildDummyNodes(graph, prevGraph);

    for (let id in anchors) {
      let edge = edges[id];
      let path = [graph[edge.srcId], anchors[id], graph[edge.dstId]];

      let prevSrcNode = prevGraph[edge.srcId];
      let prevDstNode = prevGraph[edge.dstId];
      if (prevSrcNode === undefined || prevDstNode === undefined) {
        renderEdge(path, edge.data);
      } else {
        let prevAnchor = prevAnchors[id];
        if (prevAnchor === undefined) {
          prevAnchor = {x: (prevSrcNode.x + prevDstNode.x) / 2, y: (prevSrcNode.y + prevDstNode.y) / 2};
        }
        let prevPath = [prevSrcNode, prevAnchor, prevDstNode];
        renderEdgeAnimation(prevPath, path, edge.data, duration, ease);
      }
    }

    // for vanishing edges
    for (let id in prevAnchors) {
      if (anchors[id] !== undefined) continue;
      let edge = edges[id];
      let srcNode = graph[edge.srcId];
      let dstNode = graph[edge.dstId];
      if (srcNode === undefined || dstNode === undefined) continue;
      let anchor = {x: (srcNode.x + dstNode.x) / 2, y: (srcNode.y + dstNode.y) / 2};
      let path = [srcNode, anchor, dstNode];
      let prevPath = [prevGraph[edge.srcId], prevAnchors[id], prevGraph[edge.dstId]];
      renderEdgeAnimation(prevPath, path, edge.data, duration, ease);
    }

    for (let id in graph) {
      let node = graph[id];
      let prevNode = prevGraph[id];

      if (prevNode !== undefined) {
        renderNodeAnimation(prevNode, node, nodes[id], duration, ease);
      } else {
        renderNode(node, nodes[id]);
      }
    }
  }

  function renderNode(node, data) {
    let group = svg.append('g')
    .attr('transform', 'translate(' + node.x + ',' + node.y + ')');
    drawNode(group, data);
  }

  function renderNodeAnimation(node1, node2, data, duration, ease) {
    let group = svg.append('g');
    group.attr('transform', 'translate(' + node1.x + ',' + node1.y + ')')
    drawNode(group, data);
    let animation = group.transition().duration(duration).ease(ease)
    .attr('transform', 'translate(' + node2.x + ',' + node2.y + ')');

    if (node2.dummy) {
      animation.each('end', function () {
        group.remove();
      });
    }
  }

  let lineFunc = d3.svg.line()
  .x(function (d) { return d.x; })
  .y(function (d) { return d.y; })
  .interpolate('basis');

  function renderEdge(anchors, data) {
    let mid = anchors[Math.floor(anchors.length / 2)];
    let path = svg.append('path')
    .attr('d', lineFunc(anchors)).attr('fill', 'none');
    let group = svg.append('g')
    .attr('transform', 'translate(' + mid.x + ',' + mid.y + ')');
    drawEdge(path, group, data);
  }

  function renderEdgeAnimation(anchors1, anchors2, data, duration, ease) {
    let path = svg.append('path').attr('fill', 'none');
    path.attr('d', lineFunc(anchors1));
    path.transition().duration(duration).ease(ease)
    .attr('d', lineFunc(anchors2));

    let mid1 = anchors1[Math.floor(anchors1.length / 2)];
    let mid2 = anchors2[Math.floor(anchors2.length / 2)];

    let group = svg.append('g');
    group.attr('transform', 'translate(' + mid1.x + ',' + mid1.y + ')');
    group.transition().duration(duration).ease(ease)
    .attr('transform', 'translate(' + mid2.x + ',' + mid2.y + ')');

    drawEdge(path, group, data);
  }

  return {
    addNode: addNode,
    delNode: delNode,
    addEdge: addEdge,
    delEdge: delEdge,
    redraw: redraw,
    nodes: nodes,
    edges: edges
  };
}
<commit_msg>tune sortLayers<commit_after>function d3graph (div, width, height, drawNode, drawEdge) {
  let svg = div.append('svg')
  .attr('width', width).attr('height', height);

  let nodes = {};
  let edges = {};
  let history = [];

  function addNode(id, data) {
    if (nodes[id] !== undefined) throw 'Node ' + id + ' already exists';
    nodes[id] = data;
  }
  function getNode(id) {
    let node = nodes[id];
    if (node == undefined) throw 'Node ' + id + ' does not exist';
    return node;
  }
  function delNode(nodeId) {
    nodes[nodeId] = undefined;
  }
  function addEdge(edgeId, srcId, dstId, data) {
    edges[edgeId] = {id: edgeId, srcId: srcId, dstId: dstId, data: data};
  }
  function delEdge(edgeId) {
    edges[edgeId] = undefined;
  }

  function buildGraph() {
    let graph = {};
    for (let id in nodes) {
      if (nodes[id] !== undefined) {
        graph[id] = {src:[], dst: []};
      }
    }
    for (let eid in edges) {
      let edge = edges[eid];
      if (graph[edge.srcId] !== undefined && graph[edge.dstId] != undefined) {
        graph[edge.srcId].dst.push(edge);
        graph[edge.dstId].src.push(edge);
      }
    }
    return graph;
  }

  function buildLayers(graph) {
    let queue = [];
    // first layer has no src
    for (let id in graph) {
      if (graph[id].src.length === 0) {
        queue.push(id);
      }
    }
    queue.push(undefined);

    let level = 0;
    while (queue.length > 0) {
      let id = queue.shift();
      if (id === undefined) {
        level++;
        if (queue.length === 0) break;
        queue.push(undefined);
        continue;
      }

      graph[id].level = graph[id].level || level;
      graph[id].dst.forEach(function (edge) {
        queue.push(edge.dstId);
      });
    }
    let layers = [];
    for (let i = 0; i < level; ++i) {
      layers.push([]);
    }
    for (let id in graph) {
      layers[graph[id].level].push(id);
    }
    return layers;
  }

  function sortLayers(graph, layers) {
    let round = layers.length;
    while (true) {
      layers.forEach(function (layer) {
        layer.forEach(function (id, index) {
          graph[id].y = (index + 1) / layer.length + 1;
        });
      });
      if (round-- < 0) {
        break;
      }

      layers.forEach(function (layer) {
        layer.forEach(function (id) {
          let node = graph[id];
          let sum = 0.0;
          node.src.forEach(function (edge) {
            sum += graph[edge.dstId].y;
          });
          node.dst.forEach(function (edge) {
            sum += graph[edge.srcId].y;
          });
          graph[id].y = sum / (node.src.length + node.dst.length);
        });
        layer = layer.sort(function (id1, id2) {
          return graph[id1].y - graph[id2].y;
        });
      });
    }
  }

  function layoutNodes(graph, layers) {
    layers.forEach(function (layer, i) {
      let x = width * (i + 1) / (layers.length + 1);
      layer.forEach(function (id, j) {
        let y = height * (j + 1) / (layer.length + 1);
        graph[id].x = x;
        graph[id].y = y;
      });
    });
  }

  function layoutEdges(graph, layers) {
    let edgeAnchors = {};
    for (let srcId in graph) {
      let srcNode = graph[srcId];
      let edgeGroups = {};
      srcNode.dst.forEach(function (edge) {
        if (edgeGroups[edge.dstId] === undefined) {
          edgeGroups[edge.dstId] = [];
        }
        edgeGroups[edge.dstId].push(edge);
      });

      let srcMargin = 0.5 * height / (layers[srcNode.level].length + 1);

      for (let dstId in edgeGroups) {
        let edges = edgeGroups[dstId];
        let dstNode = graph[dstId];
        let dstMargin = 0.5 * height / (layers[dstNode.level].length + 1);
        let top = (srcNode.y - srcMargin + dstNode.y - dstMargin) / 2;
        let bottom = (srcNode.y + srcMargin + dstNode.y + dstMargin) / 2;
        let step = (bottom - top) / (edges.length + 1);
        let x = (srcNode.x + dstNode.x) / 2;
        edges.forEach(function (edge, i) {
          let y = (i+1) * step + top;
          edgeAnchors[edge.id] = {x: x, y: y};
        });
      }
    }
    return edgeAnchors;
  }

  function redraw(duration, ease) {
    svg.selectAll('*').remove();

    let graph = buildGraph();
    let layers = buildLayers(graph);
    sortLayers(graph, layers);
    layoutNodes(graph, layers);
    let anchors = layoutEdges(graph, layers);

    if (duration !== undefined && history.length > 0) {
      ease = ease || 'cos';
      let prevGraph = history.shift();
      let prevAnchors = history.shift();
      renderAnimation(prevGraph, graph, prevAnchors, anchors, duration, ease);
    } else {
      render(graph, anchors);
    }

    let saveGraph = {};
    for (let id in graph) {
      if (graph[id].dummy === undefined) {
        saveGraph[id] = graph[id];
      }
    }
    history.push(saveGraph);
    history.push(anchors);
  }

  function buildDummyNodes(graph1, graph2) {
    for (let id in graph2) {
      if (graph1[id] !== undefined) continue;
      let node = graph2[id];
      // fill graph1[id] with nearest parent
      let queue = [id];
      while (queue.length > 0) {
        let pid = queue.shift();
        let parent = graph1[pid];
        if (parent !== undefined) {
          graph1[id] = {x: parent.x, y: parent.y, dummy: true};
          break;
        }
        graph2[pid].src.forEach(function (edge) {
          queue.push(edge.srcId);
        });
      }
    }
  }

  function render(graph, anchors) {
    for (let id in edges) {
      let edge = edges[id];
      let srcNode = graph[edge.srcId];
      let dstNode = graph[edge.dstId];
      let anchor = anchors[id];
      if (anchor !== undefined) {
        renderEdge([srcNode, anchor, dstNode], edge.data);
      }
    }

    for (let id in graph) {
      renderNode(graph[id], nodes[id]);
    }
  }

  function renderAnimation(prevGraph, graph, prevAnchors, anchors, duration, ease) {
    buildDummyNodes(prevGraph, graph);
    buildDummyNodes(graph, prevGraph);

    for (let id in anchors) {
      let edge = edges[id];
      let path = [graph[edge.srcId], anchors[id], graph[edge.dstId]];

      let prevSrcNode = prevGraph[edge.srcId];
      let prevDstNode = prevGraph[edge.dstId];
      if (prevSrcNode === undefined || prevDstNode === undefined) {
        renderEdge(path, edge.data);
      } else {
        let prevAnchor = prevAnchors[id];
        if (prevAnchor === undefined) {
          prevAnchor = {x: (prevSrcNode.x + prevDstNode.x) / 2, y: (prevSrcNode.y + prevDstNode.y) / 2};
        }
        let prevPath = [prevSrcNode, prevAnchor, prevDstNode];
        renderEdgeAnimation(prevPath, path, edge.data, duration, ease);
      }
    }

    // for vanishing edges
    for (let id in prevAnchors) {
      if (anchors[id] !== undefined) continue;
      let edge = edges[id];
      let srcNode = graph[edge.srcId];
      let dstNode = graph[edge.dstId];
      if (srcNode === undefined || dstNode === undefined) continue;
      let anchor = {x: (srcNode.x + dstNode.x) / 2, y: (srcNode.y + dstNode.y) / 2};
      let path = [srcNode, anchor, dstNode];
      let prevPath = [prevGraph[edge.srcId], prevAnchors[id], prevGraph[edge.dstId]];
      renderEdgeAnimation(prevPath, path, edge.data, duration, ease);
    }

    for (let id in graph) {
      let node = graph[id];
      let prevNode = prevGraph[id];

      if (prevNode !== undefined) {
        renderNodeAnimation(prevNode, node, nodes[id], duration, ease);
      } else {
        renderNode(node, nodes[id]);
      }
    }
  }

  function renderNode(node, data) {
    let group = svg.append('g')
    .attr('transform', 'translate(' + node.x + ',' + node.y + ')');
    drawNode(group, data);
  }

  function renderNodeAnimation(node1, node2, data, duration, ease) {
    let group = svg.append('g');
    group.attr('transform', 'translate(' + node1.x + ',' + node1.y + ')')
    drawNode(group, data);
    let animation = group.transition().duration(duration).ease(ease)
    .attr('transform', 'translate(' + node2.x + ',' + node2.y + ')');

    if (node2.dummy) {
      animation.each('end', function () {
        group.remove();
      });
    }
  }

  let lineFunc = d3.svg.line()
  .x(function (d) { return d.x; })
  .y(function (d) { return d.y; })
  .interpolate('basis');

  function renderEdge(anchors, data) {
    let mid = anchors[Math.floor(anchors.length / 2)];
    let path = svg.append('path')
    .attr('d', lineFunc(anchors)).attr('fill', 'none');
    let group = svg.append('g')
    .attr('transform', 'translate(' + mid.x + ',' + mid.y + ')');
    drawEdge(path, group, data);
  }

  function renderEdgeAnimation(anchors1, anchors2, data, duration, ease) {
    let path = svg.append('path').attr('fill', 'none');
    path.attr('d', lineFunc(anchors1));
    path.transition().duration(duration).ease(ease)
    .attr('d', lineFunc(anchors2));

    let mid1 = anchors1[Math.floor(anchors1.length / 2)];
    let mid2 = anchors2[Math.floor(anchors2.length / 2)];

    let group = svg.append('g');
    group.attr('transform', 'translate(' + mid1.x + ',' + mid1.y + ')');
    group.transition().duration(duration).ease(ease)
    .attr('transform', 'translate(' + mid2.x + ',' + mid2.y + ')');

    drawEdge(path, group, data);
  }

  return {
    addNode: addNode,
    delNode: delNode,
    addEdge: addEdge,
    delEdge: delEdge,
    redraw: redraw,
    nodes: nodes,
    edges: edges
  };
}
<|endoftext|>


<commit_before>/**
 * CSS-JSON Converter for JavaScript
 * Converts CSS to JSON and back.
 * Version 2.1
 *
 * Released under the MIT license.
 *
 * Copyright (c) 2013 Aram Kocharyan, http://aramk.com/

 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 documentation files (the "Software"), to deal in the Software without restriction, including without limitation
 the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
 to permit persons to whom the Software is furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in all copies or substantial portions
 of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 */

var CSSJSON = new function () {

    var base = this;

    base.init = function () {
        // String functions
        String.prototype.trim = function () {
            return this.replace(/^\s+|\s+$/g, '');
        };

        String.prototype.repeat = function (n) {
            return new Array(1 + n).join(this);
        };
    };
    base.init();

    var selX = /([^\s\;\{\}][^\;\{\}]*)\{/g;
    var endX = /\}/g;
    var lineX = /([^\;\{\}]*)\;/g;
    var commentX = /\/\*[\s\S]*?\*\//g;
    var lineAttrX = /([^\:]+):([^\;]*);/;

    // This is used, a concatenation of all above. We use alternation to
    // capture.
    var altX = /(\/\*[\s\S]*?\*\/)|([^\s\;\{\}][^\;\{\}]*(?=\{))|(\})|([^\;\{\}]+\;(?!\s*\*\/))/gmi;

    // Capture groups
    var capComment = 1;
    var capSelector = 2;
    var capEnd = 3;
    var capAttr = 4;

    var isEmpty = function (x) {
        return typeof x == 'undefined' || x.length == 0 || x == null;
    };

    var isCssJson = function (node) {
        return !isEmpty(node) ? (node.attributes && node.children) : false;
    }

    /**
     * Input is css string and current pos, returns JSON object
     *
     * @param cssString
     *            The CSS string.
     * @param args
     *            An optional argument object. ordered: Whether order of
     *            comments and other nodes should be kept in the output. This
     *            will return an object where all the keys are numbers and the
     *            values are objects containing "name" and "value" keys for each
     *            node. comments: Whether to capture comments. split: Whether to
     *            split each comma separated list of selectors.
     */
    base.toJSON = function (cssString, args) {
        var node = {
            children: {},
            attributes: {}
        };
        var match = null;
        var count = 0;

        if (typeof args == 'undefined') {
            var args = {
                ordered: false,
                comments: false,
                stripComments: false,
                split: false
            };
        }
        if (args.stripComments) {
            args.comments = false;
            cssString = cssString.replace(commentX, '');
        }

        while ((match = altX.exec(cssString)) != null) {
            if (!isEmpty(match[capComment]) && args.comments) {
                // Comment
                var add = match[capComment].trim();
                node[count++] = add;
            } else if (!isEmpty(match[capSelector])) {
                // New node, we recurse
                var name = match[capSelector].trim();
                // This will return when we encounter a closing brace
                var newNode = base.toJSON(cssString, args);
                if (args.ordered) {
                    var obj = {};
                    obj['name'] = name;
                    obj['value'] = newNode;
                    // Since we must use key as index to keep order and not
                    // name, this will differentiate between a Rule Node and an
                    // Attribute, since both contain a name and value pair.
                    obj['type'] = 'rule';
                    node[count++] = obj;
                } else {
                    if (args.split) {
                        var bits = name.split(',');
                    } else {
                        var bits = [name];
                    }
                    for (var i = 0; i < bits.length; i++) {
                        var sel = bits[i].trim();
                        if (sel in node.children) {
                            for (var att in newNode.attributes) {
                                node.children[sel].attributes[att] = newNode.attributes[att];
                            }
                        } else {
                            node.children[sel] = newNode;
                        }
                    }
                }
            } else if (!isEmpty(match[capEnd])) {
                // Node has finished
                return node;
            } else if (!isEmpty(match[capAttr])) {
                var line = match[capAttr].trim();
                var attr = lineAttrX.exec(line);
                if (attr) {
                    // Attribute
                    var name = attr[1].trim();
                    var value = attr[2].trim();
                    if (args.ordered) {
                        var obj = {};
                        obj['name'] = name;
                        obj['value'] = value;
                        obj['type'] = 'attr';
                        node[count++] = obj;
                    } else {
                        if (name in node.attributes) {
                            var currVal = node.attributes[name];
                            if (!(currVal instanceof Array)) {
                                node.attributes[name] = [currVal];
                            }
                            node.attributes[name].push(value);
                        } else {
                            node.attributes[name] = value;
                        }
                    }
                } else {
                    // Semicolon terminated line
                    node[count++] = line;
                }
            }
        }

        return node;
    };

    /**
     * @param node
     *            A JSON node.
     * @param depth
     *            The depth of the current node; used for indentation and
     *            optional.
     * @param breaks
     *            Whether to add line breaks in the output.
     */
    base.toCSS = function (node, depth, breaks) {
        var cssString = '';
        if (typeof depth == 'undefined') {
            depth = 0;
        }
        if (typeof breaks == 'undefined') {
            breaks = false;
        }
        if (node.attributes) {
            for (i in node.attributes) {
                var att = node.attributes[i];
                if (att instanceof Array) {
                    for (var j = 0; j < att.length; j++) {
                        cssString += strAttr(i, att[j], depth);
                    }
                } else {
                    cssString += strAttr(i, att, depth);
                }
            }
        }
        if (node.children) {
            var first = true;
            for (i in node.children) {
                if (breaks && !first) {
                    cssString += '\n';
                } else {
                    first = false;
                }
                cssString += strNode(i, node.children[i], depth);
            }
        }
        return cssString;
    };

    /**
     * @param data
     *            You can pass css string or the CSSJS.toJSON return value.
     * @param id (Optional)
     *            To identify and easy removable of the style element
     * @param replace (Optional. defaults to TRUE)
     *            Whether to remove or simply do nothing
     * @return HTMLLinkElement
     */
    base.toHEAD = function (data, id, replace) {
        var head = document.getElementsByTagName('head')[0];
        var xnode = document.getElementById(id);
        var _xnodeTest = (xnode !== null && xnode instanceof HTMLStyleElement);

        if (isEmpty(data) || !(head instanceof HTMLHeadElement)) return;
        if (_xnodeTest) {
            if (replace === true || isEmpty(replace)) {
                xnode.removeAttribute('id');
            } else return;
        }
        if (isCssJson(data)) {
            data = base.toCSS(data);
        }

        var node = document.createElement('style');
        node.type = 'text/css';

        if (!isEmpty(id)) {
            node.id = id;
        } else {
            node.id = 'cssjson_' + timestamp();
        }
        if (node.styleSheet) {
            node.styleSheet.cssText = data;
        } else {
            node.appendChild(document.createTextNode(data));
        }

        head.appendChild(node);

        if (isValidStyleNode(node)) {
            if (_xnodeTest) {
                xnode.parentNode.removeChild(xnode);
            }
        } else {
            node.parentNode.removeChild(node);
            if (_xnodeTest) {
                xnode.setAttribute('id', id);
                node = xnode;
            } else return;
        }

        return node;
    };

    // Alias

    if (typeof window != 'undefined') {
        window.createCSS = base.toHEAD;
    }

    // Helpers

    var isValidStyleNode = function (node) {
        return (node instanceof HTMLStyleElement) && node.sheet.cssRules.length > 0;
    }

    var timestamp = function () {
        return Date.now() || +new Date();
    };

    var strAttr = function (name, value, depth) {
        return '\t'.repeat(depth) + name + ': ' + value + ';\n';
    };

    var strNode = function (name, value, depth) {
        var cssString = '\t'.repeat(depth) + name + ' {\n';
        cssString += base.toCSS(value, depth + 1);
        cssString += '\t'.repeat(depth) + '}\n';
        return cssString;
    };

};
<commit_msg>add: the CSSJSON function is exported in module.exports, in case the module object exists to simplify the use of the module with require<commit_after>/**
 * CSS-JSON Converter for JavaScript
 * Converts CSS to JSON and back.
 * Version 2.1
 *
 * Released under the MIT license.
 *
 * Copyright (c) 2013 Aram Kocharyan, http://aramk.com/

 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 documentation files (the "Software"), to deal in the Software without restriction, including without limitation
 the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
 to permit persons to whom the Software is furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in all copies or substantial portions
 of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 */

var CSSJSON = new function () {

    var base = this;

    base.init = function () {
        // String functions
        String.prototype.trim = function () {
            return this.replace(/^\s+|\s+$/g, '');
        };

        String.prototype.repeat = function (n) {
            return new Array(1 + n).join(this);
        };
    };
    base.init();

    var selX = /([^\s\;\{\}][^\;\{\}]*)\{/g;
    var endX = /\}/g;
    var lineX = /([^\;\{\}]*)\;/g;
    var commentX = /\/\*[\s\S]*?\*\//g;
    var lineAttrX = /([^\:]+):([^\;]*);/;

    // This is used, a concatenation of all above. We use alternation to
    // capture.
    var altX = /(\/\*[\s\S]*?\*\/)|([^\s\;\{\}][^\;\{\}]*(?=\{))|(\})|([^\;\{\}]+\;(?!\s*\*\/))/gmi;

    // Capture groups
    var capComment = 1;
    var capSelector = 2;
    var capEnd = 3;
    var capAttr = 4;

    var isEmpty = function (x) {
        return typeof x == 'undefined' || x.length == 0 || x == null;
    };

    var isCssJson = function (node) {
        return !isEmpty(node) ? (node.attributes && node.children) : false;
    }

    /**
     * Input is css string and current pos, returns JSON object
     *
     * @param cssString
     *            The CSS string.
     * @param args
     *            An optional argument object. ordered: Whether order of
     *            comments and other nodes should be kept in the output. This
     *            will return an object where all the keys are numbers and the
     *            values are objects containing "name" and "value" keys for each
     *            node. comments: Whether to capture comments. split: Whether to
     *            split each comma separated list of selectors.
     */
    base.toJSON = function (cssString, args) {
        var node = {
            children: {},
            attributes: {}
        };
        var match = null;
        var count = 0;

        if (typeof args == 'undefined') {
            var args = {
                ordered: false,
                comments: false,
                stripComments: false,
                split: false
            };
        }
        if (args.stripComments) {
            args.comments = false;
            cssString = cssString.replace(commentX, '');
        }

        while ((match = altX.exec(cssString)) != null) {
            if (!isEmpty(match[capComment]) && args.comments) {
                // Comment
                var add = match[capComment].trim();
                node[count++] = add;
            } else if (!isEmpty(match[capSelector])) {
                // New node, we recurse
                var name = match[capSelector].trim();
                // This will return when we encounter a closing brace
                var newNode = base.toJSON(cssString, args);
                if (args.ordered) {
                    var obj = {};
                    obj['name'] = name;
                    obj['value'] = newNode;
                    // Since we must use key as index to keep order and not
                    // name, this will differentiate between a Rule Node and an
                    // Attribute, since both contain a name and value pair.
                    obj['type'] = 'rule';
                    node[count++] = obj;
                } else {
                    if (args.split) {
                        var bits = name.split(',');
                    } else {
                        var bits = [name];
                    }
                    for (var i = 0; i < bits.length; i++) {
                        var sel = bits[i].trim();
                        if (sel in node.children) {
                            for (var att in newNode.attributes) {
                                node.children[sel].attributes[att] = newNode.attributes[att];
                            }
                        } else {
                            node.children[sel] = newNode;
                        }
                    }
                }
            } else if (!isEmpty(match[capEnd])) {
                // Node has finished
                return node;
            } else if (!isEmpty(match[capAttr])) {
                var line = match[capAttr].trim();
                var attr = lineAttrX.exec(line);
                if (attr) {
                    // Attribute
                    var name = attr[1].trim();
                    var value = attr[2].trim();
                    if (args.ordered) {
                        var obj = {};
                        obj['name'] = name;
                        obj['value'] = value;
                        obj['type'] = 'attr';
                        node[count++] = obj;
                    } else {
                        if (name in node.attributes) {
                            var currVal = node.attributes[name];
                            if (!(currVal instanceof Array)) {
                                node.attributes[name] = [currVal];
                            }
                            node.attributes[name].push(value);
                        } else {
                            node.attributes[name] = value;
                        }
                    }
                } else {
                    // Semicolon terminated line
                    node[count++] = line;
                }
            }
        }

        return node;
    };

    /**
     * @param node
     *            A JSON node.
     * @param depth
     *            The depth of the current node; used for indentation and
     *            optional.
     * @param breaks
     *            Whether to add line breaks in the output.
     */
    base.toCSS = function (node, depth, breaks) {
        var cssString = '';
        if (typeof depth == 'undefined') {
            depth = 0;
        }
        if (typeof breaks == 'undefined') {
            breaks = false;
        }
        if (node.attributes) {
            for (i in node.attributes) {
                var att = node.attributes[i];
                if (att instanceof Array) {
                    for (var j = 0; j < att.length; j++) {
                        cssString += strAttr(i, att[j], depth);
                    }
                } else {
                    cssString += strAttr(i, att, depth);
                }
            }
        }
        if (node.children) {
            var first = true;
            for (i in node.children) {
                if (breaks && !first) {
                    cssString += '\n';
                } else {
                    first = false;
                }
                cssString += strNode(i, node.children[i], depth);
            }
        }
        return cssString;
    };

    /**
     * @param data
     *            You can pass css string or the CSSJS.toJSON return value.
     * @param id (Optional)
     *            To identify and easy removable of the style element
     * @param replace (Optional. defaults to TRUE)
     *            Whether to remove or simply do nothing
     * @return HTMLLinkElement
     */
    base.toHEAD = function (data, id, replace) {
        var head = document.getElementsByTagName('head')[0];
        var xnode = document.getElementById(id);
        var _xnodeTest = (xnode !== null && xnode instanceof HTMLStyleElement);

        if (isEmpty(data) || !(head instanceof HTMLHeadElement)) return;
        if (_xnodeTest) {
            if (replace === true || isEmpty(replace)) {
                xnode.removeAttribute('id');
            } else return;
        }
        if (isCssJson(data)) {
            data = base.toCSS(data);
        }

        var node = document.createElement('style');
        node.type = 'text/css';

        if (!isEmpty(id)) {
            node.id = id;
        } else {
            node.id = 'cssjson_' + timestamp();
        }
        if (node.styleSheet) {
            node.styleSheet.cssText = data;
        } else {
            node.appendChild(document.createTextNode(data));
        }

        head.appendChild(node);

        if (isValidStyleNode(node)) {
            if (_xnodeTest) {
                xnode.parentNode.removeChild(xnode);
            }
        } else {
            node.parentNode.removeChild(node);
            if (_xnodeTest) {
                xnode.setAttribute('id', id);
                node = xnode;
            } else return;
        }

        return node;
    };

    // Alias

    if (typeof window != 'undefined') {
        window.createCSS = base.toHEAD;
    }

    // Helpers

    var isValidStyleNode = function (node) {
        return (node instanceof HTMLStyleElement) && node.sheet.cssRules.length > 0;
    }

    var timestamp = function () {
        return Date.now() || +new Date();
    };

    var strAttr = function (name, value, depth) {
        return '\t'.repeat(depth) + name + ': ' + value + ';\n';
    };

    var strNode = function (name, value, depth) {
        var cssString = '\t'.repeat(depth) + name + ' {\n';
        cssString += base.toCSS(value, depth + 1);
        cssString += '\t'.repeat(depth) + '}\n';
        return cssString;
    };

};

if (typeof module == 'object')
    module.exports = CSSJSON;<|endoftext|>


<commit_before>/**
 * CSSugar Constructor.
 *
 * @param {Object} cssjson An object literal representation of CSS folllowing CSS JSON convention (http://featureblend.com/css-json.html).
 *
 *        Example:
 *        {
 *            "selector-1": {
 *                "property-1" : "value-1",
 *                "property-n" : "value-n"
 *            },
 *            "selector-n": {
 *                "property-1" : "value-1",
 *                "property-n" : "value-n"
 *            }
 *        }
 *
 * @param {Function} selectorEngine
 *        A function to be used to handle the selector matching.
 *        The function gets passed two arguments:
 *        {String} selector The selector.
 *        {DOMElement} context A DOM element to constrain the selector context, defaults
 *        to document if not defined in options selectorContext.
 *        The function should return an Array of matching DOM element references.
 *        If no matches are found, an empty Array is expected.
 *
 * @param {Object} (Optional) options An object literal representation of options.
 *        Options:
 *        {DOMElement} selectorContext A DOM element to constrain the selector context.
 *        {Function} processor A function used to handle the processing of
 *        property/value pairs. The function gets passed one argument:
 *        {Object} An object literal having the following properties:
 *        {String} property The property name.
 *        {String} value The property value.
 *        {String} selector The governing selector.
 *        {Object} cssjson The invoked cssjson object. See constructor argument cssjson documentation.
 *        The function should return an object literal of property/values for replacement.
 *        To remove the property/value completely return and an empty object literal (eg., {})
 */
function CSSugar(cssjson, selectorEngine){
    var options = arguments[2] || {};
    for(var selector in cssjson){
        if(cssjson.hasOwnProperty(selector)){
            var elements = selectorEngine(selector, options.selectorContext || document);
            var properties = cssjson[selector]
            for(var i=0; i<elements.length; i++){
                var element = elements[i];
                for(var property in properties){
                    if(properties.hasOwnProperty(property)){
                        if(options.processor){
                            var processorArguments = {
                                "property": property,
                                "value": properties[property],
                                "selector": selector,
                                "cssjson": cssjson
                            };
                            var processedProperties = options.processor(processorArguments);
                            for(var processedProperty in processedProperties){
                                if(processedProperties.hasOwnProperty(processedProperty)){
                                    element.style[processedProperty] = processedProperties[processedProperty];
                                }
                            }
                        }else{
                            element.style[property] = properties[property];
                        }
                    }
                }
            }
        }
    }
}<commit_msg>Add explicity hander for float property remap to cssFloat.<commit_after>/**
 * CSSugar Constructor.
 *
 * @param {Object} cssjson An object literal representation of CSS folllowing CSS JSON convention (http://featureblend.com/css-json.html).
 *
 *        Example:
 *        {
 *            "selector-1": {
 *                "property-1" : "value-1",
 *                "property-n" : "value-n"
 *            },
 *            "selector-n": {
 *                "property-1" : "value-1",
 *                "property-n" : "value-n"
 *            }
 *        }
 *
 * @param {Function} selectorEngine
 *        A function to be used to handle the selector matching.
 *        The function gets passed two arguments:
 *        {String} selector The selector.
 *        {DOMElement} context A DOM element to constrain the selector context, defaults
 *        to document if not defined in options selectorContext.
 *        The function should return an Array of matching DOM element references.
 *        If no matches are found, an empty Array is expected.
 *
 * @param {Object} (Optional) options An object literal representation of options.
 *        Options:
 *        {DOMElement} selectorContext A DOM element to constrain the selector context.
 *        {Function} processor A function used to handle the processing of
 *        property/value pairs. The function gets passed one argument:
 *        {Object} An object literal having the following properties:
 *        {String} property The property name.
 *        {String} value The property value.
 *        {String} selector The governing selector.
 *        {Object} cssjson The invoked cssjson object. See constructor argument cssjson documentation.
 *        The function should return an object literal of property/values for replacement.
 *        To remove the property/value completely return and an empty object literal (eg., {})
 */
function CSSugar(cssjson, selectorEngine){
    var options = arguments[2] || {};
    for(var selector in cssjson){
        if(cssjson.hasOwnProperty(selector)){
            var elements = selectorEngine(selector, options.selectorContext || document);
            var properties = cssjson[selector]
            for(var i=0; i<elements.length; i++){
                var element = elements[i];
                for(var property in properties){
                    if(properties.hasOwnProperty(property)){
                        if(options.processor){
                            var processorArguments = {
                                "property": property,
                                "value": properties[property],
                                "selector": selector,
                                "cssjson": cssjson
                            };
                            var processedProperties = options.processor(processorArguments);
                            for(var processedProperty in processedProperties){
                                if(processedProperties.hasOwnProperty(processedProperty)){
                                    element.style[processedProperty] = processedProperties[processedProperty];
                                }
                            }
                        }else{
                            element.style[((property=="float")?"cssFloat":property)] = properties[property];
                        }
                    }
                }
            }
        }
    }
}<|endoftext|>


<commit_before>/**
 * csvjson.js - A script to convert between CSV and JSON formats
 * Author: Aaron Snoswell (@aaronsnoswell, elucidatedbianry.com)
 */

// Namespace
var csvjson = {};

// Hide from global scope
(function(){
	function isdef(ob) {
		if(typeof(ob) == "undefined") return false;
		return true;
	}

	/**
	 * splitCSV function (c) 2009 Brian Huisman, see http://www.greywyvern.com/?post=258
	 * Works by spliting on seperators first, then patching together quoted values
	 */
	function splitCSV(str, sep) {
        for (var foo = str.split(sep = sep || ","), x = foo.length - 1, tl; x >= 0; x--) {
            if (foo[x].replace(/"\s+$/, '"').charAt(foo[x].length - 1) == '"') {
                if ((tl = foo[x].replace(/^\s+"/, '"')).length > 1 && tl.charAt(0) == '"') {
                    foo[x] = foo[x].replace(/^\s*"|"\s*$/g, '').replace(/""/g, '"');
                } else if (x) {
                    foo.splice(x - 1, 2, [foo[x - 1], foo[x]].join(sep));
                } else foo = foo.shift().split(sep).concat(foo);
            } else foo[x].replace(/""/g, '"');
        } return foo;
    };

	/**
	 * Converts from CSV formatted data (as a string) to JSON returning
	 * 	an object.
	 * @required csvdata {string} The CSV data, formatted as a string.
	 * @param args.delim {string} The delimiter used to seperate CSV
	 * 	items. Defauts to ','.
	 * @param args.textdelim {string} The delimiter used to wrap text in
	 * 	the CSV data. Defaults to nothing (an empty string).
	 */
	csvjson.csv2json = function(csvdata, args) {
		args = args || {};
		var delim = isdef(args.delim) ? args.delim : ",";
		//var textdelim = isdef(args.textdelim) ? args.textdelim : "";

		var csvlines = csvdata.split("\n");
		var csvheaders = splitCSV(csvlines[0], delim);
		var csvrows = csvlines.slice(1, csvlines.length);

		var ret = {};
		ret.headers = csvheaders;
		ret.rows = [];

		for(var r in csvrows) {
			var row = csvrows[r];
			var rowitems = splitCSV(row, delim);

			// Break if we're at the end of the file
			if(row.length == 0) break;

			var rowob = {};
			for(var i in rowitems) {
				var item = rowitems[i];

				// Remove any text delimiters present in the items
				//if(textdelim.length !== 0) item = item.replace(new RegExp(textdelim, 'g'), "");

				// ...and try to (intelligently) cast the item to a number
				if(!isNaN(item*1)) {
					item = item*1;
				}

				rowob[csvheaders[i]] = item;
			}

			ret.rows.push(rowob);
		}

		return ret;
	}	// end csv2json

	/**
	 * Taken an object of the form
	 * {
	 *     headers: ["Heading 1", "Header 2", ...]
	 *     rows: [
	 *	       {"Heading 1": SomeValue, "Heading 2": SomeOtherValue},
	 *	       {"Heading 1": SomeValue, "Heading 2": SomeOtherValue},
	 *         ...
	 *     ]
	 * }
	 * and returns a CSV representation as a string.
	 * @requires jsondata {object} The JSON object to parse.
	 * @param args.delim {string} The delimiter used to seperate CSV
	 * 	items. Defauts to ','.
	 * @param args.textdelim {string} The delimiter used to wrap text in
	 * 	the CSV data. Defaults to nothing (an empty string).
	 */
	csvjson.json2csv = function(jsondata, args) {
		args = args || {};
		var delim = isdef(args.delim) ? args.delim : ",";
		var textdelim = isdef(args.textdelim) ? args.textdelim : "";

		if(typeof(jsondata) == "string") {
			// JSON text parsing is not implemented (yet)
			return null;
		}

		var ret = "";

		// Add the headers
		for(var h in jsondata.headers) {
			var heading = jsondata.headers[h];
			ret += textdelim + heading + textdelim +  delim;
		}

		// Remove trailing delimiter
		ret = ret.slice(0, ret.length-1);
		ret += "\n";

		// Add the items
		for(var r in jsondata.rows) {
			var row = jsondata.rows[r];

			// Only add elements that are mentioned in the headers (in order, obviously)
			for(var h in jsondata.headers) {
				var heading = jsondata.headers[h];
				var data = row[heading];

				if(typeof(data) == "string") {
					ret += textdelim + row[heading] + textdelim +  delim;
				} else {
					ret += row[heading] + delim;
				}
			}

			// Remove trailing delimiter
			ret = ret.slice(0, ret.length-1);
			ret += "\n";
		}

		// Remove trailing newling
		ret = ret.slice(0, ret.length-1);

		return ret;
	}

})();	// Execute hidden-scope code

<commit_msg>Updated code and removed the textdelim argument from csv2json() - text delimiters are now automatically detected<commit_after>/**
 * csvjson.js - A script to convert between CSV and JSON formats
 * Author: Aaron Snoswell (@aaronsnoswell, elucidatedbianry.com)
 */

// Namespace
var csvjson = {};

// Hide from global scope
(function(){
	function isdef(ob) {
		if(typeof(ob) == "undefined") return false;
		return true;
	}

	/**
	 * splitCSV function (c) 2009 Brian Huisman, see http://www.greywyvern.com/?post=258
	 * Works by spliting on seperators first, then patching together quoted values
	 */
	function splitCSV(str, sep) {
        for (var foo = str.split(sep = sep || ","), x = foo.length - 1, tl; x >= 0; x--) {
            if (foo[x].replace(/"\s+$/, '"').charAt(foo[x].length - 1) == '"') {
                if ((tl = foo[x].replace(/^\s+"/, '"')).length > 1 && tl.charAt(0) == '"') {
                    foo[x] = foo[x].replace(/^\s*"|"\s*$/g, '').replace(/""/g, '"');
                } else if (x) {
                    foo.splice(x - 1, 2, [foo[x - 1], foo[x]].join(sep));
                } else foo = foo.shift().split(sep).concat(foo);
            } else foo[x].replace(/""/g, '"');
        } return foo;
    };

	/**
	 * Converts from CSV formatted data (as a string) to JSON returning
	 * 	an object.
	 * @required csvdata {string} The CSV data, formatted as a string.
	 * @param args.delim {string} The delimiter used to seperate CSV
	 * 	items. Defauts to ','.
	 * @param args.textdelim {string} The delimiter used to wrap text in
	 * 	the CSV data. Defaults to nothing (an empty string).
	 */
	csvjson.csv2json = function(csvdata, args) {
		args = args || {};
		var delim = isdef(args.delim) ? args.delim : ",";
		// Unused
		//var textdelim = isdef(args.textdelim) ? args.textdelim : "";

		var csvlines = csvdata.split("\n");
		var csvheaders = splitCSV(csvlines[0], delim);
		var csvrows = csvlines.slice(1, csvlines.length);

		var ret = {};
		ret.headers = csvheaders;
		ret.rows = [];

		for(var r in csvrows) {
			var row = csvrows[r];
			var rowitems = splitCSV(row, delim);

			// Break if we're at the end of the file
			if(row.length == 0) break;

			var rowob = {};
			for(var i in rowitems) {
				var item = rowitems[i];

				// Try to (intelligently) cast the item to a number, if applicable
				if(!isNaN(item*1)) {
					item = item*1;
				}

				rowob[csvheaders[i]] = item;
			}

			ret.rows.push(rowob);
		}

		return ret;
	}	// end csv2json

	/**
	 * Taken an object of the form
	 * {
	 *     headers: ["Heading 1", "Header 2", ...]
	 *     rows: [
	 *	       {"Heading 1": SomeValue, "Heading 2": SomeOtherValue},
	 *	       {"Heading 1": SomeValue, "Heading 2": SomeOtherValue},
	 *         ...
	 *     ]
	 * }
	 * and returns a CSV representation as a string.
	 * @requires jsondata {object} The JSON object to parse.
	 * @param args.delim {string} The delimiter used to seperate CSV
	 * 	items. Defauts to ','.
	 * @param args.textdelim {string} The delimiter used to wrap text in
	 * 	the CSV data. Defaults to nothing (an empty string).
	 */
	csvjson.json2csv = function(jsondata, args) {
		args = args || {};
		var delim = isdef(args.delim) ? args.delim : ",";
		var textdelim = isdef(args.textdelim) ? args.textdelim : "";

		if(typeof(jsondata) == "string") {
			// JSON text parsing is not implemented (yet)
			return null;
		}

		var ret = "";

		// Add the headers
		for(var h in jsondata.headers) {
			var heading = jsondata.headers[h];
			ret += textdelim + heading + textdelim +  delim;
		}

		// Remove trailing delimiter
		ret = ret.slice(0, ret.length-1);
		ret += "\n";

		// Add the items
		for(var r in jsondata.rows) {
			var row = jsondata.rows[r];

			// Only add elements that are mentioned in the headers (in order, obviously)
			for(var h in jsondata.headers) {
				var heading = jsondata.headers[h];
				var data = row[heading];

				if(typeof(data) == "string") {
					ret += textdelim + row[heading] + textdelim +  delim;
				} else {
					ret += row[heading] + delim;
				}
			}

			// Remove trailing delimiter
			ret = ret.slice(0, ret.length-1);
			ret += "\n";
		}

		// Remove trailing newling
		ret = ret.slice(0, ret.length-1);

		return ret;
	}

})();	// Execute hidden-scope code

<|endoftext|>


<commit_before>
/* Convert a NodeList to Array */
function toArray(nl){
    return Array.prototype.slice.call(nl, 0);
}

/* DOMContentLoaded event listener */
window.addEventListener("DOMContentLoaded", function(){

    function dropdownGroup(btnGroup, callback){
        var view = btnGroup.querySelector("[data-view]");
        var list = btnGroup.querySelector("[data-list]");

        callback = callback || function(){};

        btnGroup.addEventListener("click", function(e){
            var selected = btnGroup.querySelector("[data-selected]");
            var targetValue = e.target.getAttribute("data-value");
            var activeDropdown = document.querySelector(".active_dropdown");

            // opens up the current selected dropdown list
            btnGroup.querySelector(".dropdown_options").classList.toggle("collapsed");
            btnGroup.classList.toggle("active_dropdown");

            // when user selects an option from the dropdown list
            if ( targetValue ){
                view.querySelector("a:not(.invi_focus)").innerHTML = e.target.innerHTML;
                selected.removeAttribute("data-selected");
                e.target.setAttribute("data-selected", true);
                callback(targetValue);
            }

        }, false);
    }

    /* Bind click event listener to each of the btn_group memebers */
    var btnGroupArray = toArray(document.querySelectorAll(".btn_group"));
    btnGroupArray.forEach(function(btnGroup){
        dropdownGroup(btnGroup, function(val){
            val = val.toLowerCase();
            switch(val){
                case 'clock':
                case 'graph':
                case 'list':
                    switchVisualization(val);
                    break;
                default:
                    console.log("selected val=" + val);
            }
        });
    });

    /* Toggle Info Panel */
    document.querySelector(".show-info-button").addEventListener("click", function(){
        document.querySelector("#content").classList.toggle("showinfo");
    });

    /* When a open dropdown list loses focus, collapse it. */
    window.addEventListener("click", function(e){
        var activeDropdown = document.querySelector(".active_dropdown");
        if ( activeDropdown && !activeDropdown.contains(e.target) ){
                activeDropdown.querySelector(".dropdown_options").classList.add("collapsed");
                activeDropdown.classList.remove("active_dropdown");
        }
    }, true);

});

document.querySelector(".download").addEventListener('click', function() {
    console.log('received export data');
    window.open('data:application/json,' + exportFormat(allConnections));
});

document.querySelector('.reset-data').addEventListener('click', function(){
    addon.emit('reset');
    aggregate.emit('reset');
    currentVisualization.emit('reset');
    allConnections = [];
    delete localStorage.connections;
    delete localStorage.tempConnections;
    delete localStorage.totalNumConnections;
    // FIXME: empty the data from current view too
});

var uploadButton = document.querySelector('.upload');
if (localStorage.userHasOptedIntoSharing && localStorage.userHasOptedIntoSharing === 'true'){
    uploadButton.innerHTML = 'Stop Sharing';
}

uploadButton.addEventListener('click', function(){
    if (localStorage.userHasOptedIntoSharing && localStorage.userHasOptedIntoSharing === 'true'){
        stopSharing();
    }else{
        startSharing();
    }
});

document.querySelector('.disclosure').addEventListener('click', function(event){
    console.log('toggling filter open/closed');
    var filterSection = document.querySelector('.info .filters');
    filterSection.classList.toggle('closed');
});

function getZoom(canvas){
    // TODO: code cleanup if both cases use basically the same code
    switch(canvas){
        case 'vizcanvas': {
            var box = document.querySelector('.vizcanvas')
                        .getAttribute('viewBox')
                        .split(/\s/)
                        .map(function(i){ return parseInt(i, 10); });
            return {x: box[0], y: box[1], w: box[2], h: box[3]};
        }
        case 'mapcanvas': {
            var box = document.querySelector('.mapcanvas')
                        .getAttribute('viewBox')
                        .split(/\s/)
                        .map(function(i){ return parseInt(i, 10); });
            console.log(box);
            return {x: box[0], y: box[1], w: box[2], h: box[3]};
        }
        default: throw new Error('It has to be one of the choices above');
    }
}

function setZoom(box,canvas){
    // TODO: code cleanup if both cases use basically the same code
    switch(canvas){
        case 'vizcanvas': {
                document.querySelector('.vizcanvas')
                    .setAttribute('viewBox', [box.x, box.y, box.w, box.h].join(' '));
                break;
        }
        case 'mapcanvas': {
                document.querySelector('.mapcanvas')
                    .setAttribute('viewBox', [box.x, box.y, box.w, box.h].join(' '));
                break;

        }
        default: throw new Error('It has to be one of the choices above');
    }
}

/* Scroll over visualization to zoom in/out ========================= */

/* define viewBox limits
*  graph view default viewBox = " 0 0 1000 1000 "
*  clock                      = " -350 -495 700 500 "
*  map                        = " 0 0 2711.3 1196.7 "
*/
var graphZoomInLimit   = { x:300, y:300, w:200, h:300 };
var graphZoomOutLimit  = { w:4000, h:4000 };
var clockZoomInLimit   = { w:560, h:400 };
var clockZoomOutLimit  = { w:2800, h:2800 };
var mapZoomInLimit     = { w:(2711.3/5), h:(1196.7/5) };
var mapZoomOutLimit    = { w:2711.3, h:1196.7 };

document.querySelector(".stage").addEventListener("wheel",function(event){
    if ( event.target.mozMatchesSelector(".vizcanvas, .vizcanvas *") && currentVisualization.name != "list" ){
        if ( currentVisualization.name == "graph" ){
            zoomWithinLimit(event,"vizcanvas", graphZoomInLimit, graphZoomOutLimit);
        }else{ // clock view
            zoomWithinLimit(event,"vizcanvas", clockZoomInLimit, clockZoomOutLimit);
        }
    }
},false);

document.querySelector(".world-map").addEventListener("wheel",function(event){
    if ( event.target.mozMatchesSelector(".mapcanvas, .mapcanvas *") ){
        zoomWithinLimit(event,"mapcanvas", mapZoomInLimit, mapZoomOutLimit );
    }
},false);

// Check to see if the viewBox of the targeting svg is within the limit we define
// if yes, zoom
function zoomWithinLimit(event, targetSvg, zoomInLimit, zoomOutLimit){
    var currentViewBox = getZoom(targetSvg);

    var withinZoomInLimit = ( currentViewBox.w > zoomInLimit.w && currentViewBox.h > zoomInLimit.h);
    if ( zoomInLimit.x && zoomInLimit.y ){
        withinZoomInLimit =
            withinZoomInLimit && ( currentViewBox.x < zoomInLimit.x && currentViewBox.y < zoomInLimit.y );
    }

    var withinZoomOutLimit = ( currentViewBox.w <= zoomOutLimit.w && currentViewBox.h <= zoomOutLimit.h );

    // event.deltaY can only be larger than 1.0 or less than -1.0
    // conditions set to +/- 3 to lower the scrolling control sensitivity
    if ( event.deltaY >= 3 && withinZoomOutLimit ){ // scroll up to zoom out
        svgZooming(targetSvg, (1/1.25));
    }
    if ( event.deltaY <= -3 && withinZoomInLimit) { // scroll down to zoom in
        svgZooming(targetSvg, 1.25);
    }
}

// Apply zoom level
function svgZooming(target,ratio){

    function generateNewViewBox(target, box){
        var oldWidth = box.w;
        var newWidth = oldWidth / ratio;
        var offsetX = ( newWidth - oldWidth ) / 2;

        var oldHeight = box.h;
        var newHeight = oldHeight / ratio;
        var offsetY = ( newHeight - oldHeight ) / 2;

        box.w = box.w / ratio;
        box.h = box.h / ratio;
        box.x = box.x - offsetX;

        if ( target == "vizcanvas" ){
            box.y = ( currentVisualization.name == "graph") ? (box.y - offsetY) : -1 * (box.h - 5);
        }else{
            box.y = box.y - offsetY;
        }

        return box;
    }

    if ( target == "vizcanvas" ){
        var box = getZoom("vizcanvas");
        var newViewBox = generateNewViewBox(target, box);
        setZoom(newViewBox,"vizcanvas");

    }else{
        var box = getZoom("mapcanvas");
        var newViewBox = generateNewViewBox(target, box);
        setZoom(newViewBox,"mapcanvas");
    }

}

/* Pan by dragging ======================================== */

var onDragGraph = false;
var onDragMap = false;
var graphDragStart = {};
var mapDragStart = {};

/* vizcanvas */
document.querySelector(".stage").addEventListener("mousedown",function(event){
    if ( event.target.mozMatchesSelector(".vizcanvas, .vizcanvas *") && !event.target.mozMatchesSelector(".node, .node *") ){
        onDragGraph = true;
        graphDragStart.x = event.clientX;
        graphDragStart.y = event.clientY;
    }

},false);

document.querySelector(".stage").addEventListener("mousemove",function(event){
    if ( event.target.mozMatchesSelector(".vizcanvas") && !event.target.mozMatchesSelector(".node, .node *") && onDragGraph ){
        document.querySelector(".vizcanvas").style.cursor = "-moz-grab";
        var offsetX = ( Math.ceil(event.clientX) - graphDragStart.x );
        var offsetY = ( Math.ceil(event.clientY) - graphDragStart.y );
        var box = getZoom("vizcanvas");
        box.x -= ( offsetX * box.w/700);
        box.y -= ( offsetY * box.h/700);
        graphDragStart.x += offsetX;
        graphDragStart.y += offsetY;
        setZoom(box,"vizcanvas");
    }

},false);

document.querySelector(".stage").addEventListener("mouseup",function(event){
    onDragGraph = false;
    document.querySelector(".vizcanvas").style.cursor = "default";
},false);

document.querySelector(".stage").addEventListener("mouseleave",function(event){
    onDragGraph = false;
    document.querySelector(".vizcanvas").style.cursor = "default";
},false);

/* mapcanvas */
document.querySelector(".world-map").addEventListener("mousedown",function(event){
    if ( event.target.mozMatchesSelector(".mapcanvas, .mapcanvas *") ){
        onDragMap = true;
        mapDragStart.x = event.clientX;
        mapDragStart.y = event.clientY;
    }
},false);

document.querySelector(".world-map").addEventListener("mousemove",function(event){
    if ( event.target.mozMatchesSelector(".mapcanvas, .mapcanvas *") && onDragMap ){
        document.querySelector(".mapcanvas").style.cursor = "-moz-grab";
        var offsetX = ( Math.ceil(event.clientX) - mapDragStart.x );
        var offsetY = ( Math.ceil(event.clientY) - mapDragStart.y );
        var box = getZoom("mapcanvas");
        box.x -= (offsetX * 10);
        box.y -= (offsetY * 10);
        mapDragStart.x += offsetX;
        mapDragStart.y += offsetY;
        setZoom(box,"mapcanvas");
    }

},false);

document.querySelector(".world-map").addEventListener("mouseup",function(event){
    onDragMap = false;
    document.querySelector(".mapcanvas").style.cursor = "default";
},false);

document.querySelector(".world-map").addEventListener("mouseleave",function(event){
    onDragMap = false;
    document.querySelector(".mapcanvas").style.cursor = "default";
},false);

/* Help Mode ========================= */
document.querySelector(".help-mode").checked = false;
document.querySelector(".help-mode").addEventListener("click", function(){
    if( this.checked ){
        triggerHelp(document.querySelector("body"), "toggleOnHelp", currentVisualization.name);
    }else{
        triggerHelp(document.querySelector("body"), "toggleOffHelp", currentVisualization.name);
    }
});

/* Settings Page ========================= */
document.querySelector(".settings").addEventListener("click", function(event){
    if ( currentVisualization.name == "clock" || currentVisualization.name == "graph" ){
        document.querySelector(".vizcanvas").classList.toggle("hide");
    }else{
        document.querySelector(".list-breadcrumb").classList.toggle("hide");
        document.querySelector(".list-header").classList.toggle("hide");
        document.querySelector(".list-table").classList.toggle("hide");

    }
    var infoBarVisible = document.querySelector("#content").classList.contains("showinfo");
    if ( infoBarVisible ){
        document.querySelector("#content").classList.remove("showinfo");
    }
    document.querySelector(".settings-page").classList.toggle("hide");
});

document.querySelector(".settings-page").addEventListener("click", function(event){
    if (event.target.mozMatchesSelector(".settings-page ul li, .settings-page ul li *")){
        var site = event.target;
        while(site.mozMatchesSelector(".settings-page ul li *")){
            site = site.parentElement;
        }
        site.querySelector(".settings-option").classList.toggle("hide");
        site.querySelector(".icon-caret-right").parentElement.classList.toggle("hide");
        site.querySelector(".icon-caret-down").parentElement.classList.toggle("hide");
    }
},false);

/* Clock View ===================================== */

function highlightColludedNode(selection){
    selection.each(function(){
        var colludedNode = d3.select(this);
        if ( colludedNode.classed("source") ){  // this instance of colluded node is a source node
            colludedNode.classed("colluded-source", true);
        }
        if ( colludedNode.classed("target") ){ // this instance of colluded node is a target node
            colludedNode.classed("colluded-target", true);
        }
    });
}

function applyHighlightingEffect(clickedNodeName){
    // reset styling effect
    d3.selectAll("g.node").classed("clicked-node", false)
                          .classed("colluded-source", false)
                          .classed("colluded-target", false);

    // highlight all instances of the clicked node(both source and target)
    d3.selectAll("g[data-name='" + clickedNodeName +"']")
            .classed("clicked-node", true);

    // find all the colluded sites and highlight all instances of them
    for ( var key in aggregate.nodeForKey( clickedNodeName ) ){
        if ( key != clickedNodeName ){
            d3.selectAll("g[data-name='"+ key +"']").call(highlightColludedNode);
        }
    }

}

document.querySelector('#content').addEventListener('click', function(event){
    /*
    *   When a node in the clock visualization is clicked,
    *       all instances of the same node across the day should be highlighted
    *       all colluded nodes should also be highlighted (differently)
    */
    if ( currentVisualization.name == "clock" ){
        // click could happen on .node or an element inside of .node
        if (event.target.mozMatchesSelector('.node, .node *')){
            var node = event.target;
            while(node.mozMatchesSelector('.node *')){
                node = node.parentElement;
            }
            applyHighlightingEffect(node.getAttribute("data-name"));
        }
    }
},false);

/* Export ========== */

function exportFormat(connections){
    return JSON.stringify({
        format: 'Collusion Save File',
        version: '1.1',
        token: localStorage.collusionToken,
        connections: excludePrivateConnection(connections)
    });
}

/* Filter out connections collected in Private Mode */
function excludePrivateConnection(connections){
    return connections.filter(function(connection){
        return (connection[FROM_PRIVATE_MODE] == null);
    })
}

/* Info Panel Connections List ===================================== */

document.querySelector(".connections-list ul").addEventListener("click", function(event){
    if (event.target.mozMatchesSelector("li")){
        if ( currentVisualization.name === "clock" ){
            applyHighlightingEffect(event.target.innerHTML);
        }
        else if ( currentVisualization.name === "list" ){
            currentVisualization.emit("showFilteredTable", event.target.innerHTML);
        }else{

        }
    }
});
<commit_msg>fix for #236, downloads data directly now<commit_after>
/* Convert a NodeList to Array */
function toArray(nl){
    return Array.prototype.slice.call(nl, 0);
}

/* DOMContentLoaded event listener */
window.addEventListener("DOMContentLoaded", function(){

    function dropdownGroup(btnGroup, callback){
        var view = btnGroup.querySelector("[data-view]");
        var list = btnGroup.querySelector("[data-list]");

        callback = callback || function(){};

        btnGroup.addEventListener("click", function(e){
            var selected = btnGroup.querySelector("[data-selected]");
            var targetValue = e.target.getAttribute("data-value");
            var activeDropdown = document.querySelector(".active_dropdown");

            // opens up the current selected dropdown list
            btnGroup.querySelector(".dropdown_options").classList.toggle("collapsed");
            btnGroup.classList.toggle("active_dropdown");

            // when user selects an option from the dropdown list
            if ( targetValue ){
                view.querySelector("a:not(.invi_focus)").innerHTML = e.target.innerHTML;
                selected.removeAttribute("data-selected");
                e.target.setAttribute("data-selected", true);
                callback(targetValue);
            }

        }, false);
    }

    /* Bind click event listener to each of the btn_group memebers */
    var btnGroupArray = toArray(document.querySelectorAll(".btn_group"));
    btnGroupArray.forEach(function(btnGroup){
        dropdownGroup(btnGroup, function(val){
            val = val.toLowerCase();
            switch(val){
                case 'clock':
                case 'graph':
                case 'list':
                    switchVisualization(val);
                    break;
                default:
                    console.log("selected val=" + val);
            }
        });
    });

    /* Toggle Info Panel */
    document.querySelector(".show-info-button").addEventListener("click", function(){
        document.querySelector("#content").classList.toggle("showinfo");
    });

    /* When a open dropdown list loses focus, collapse it. */
    window.addEventListener("click", function(e){
        var activeDropdown = document.querySelector(".active_dropdown");
        if ( activeDropdown && !activeDropdown.contains(e.target) ){
                activeDropdown.querySelector(".dropdown_options").classList.add("collapsed");
                activeDropdown.classList.remove("active_dropdown");
        }
    }, true);

});

document.querySelector(".download").addEventListener('click', function(evt) {
    console.log('received export data');
    var file = new Blob([exportFormat(allConnections)], {type: 'application/json'});
    var reader = new FileReader();
    var a = document.createElement('a');
    reader.onloadend = function(){
        a.href = reader.result;
        a.download = 'collusionData.json';
        a.target = '_blank';
        document.body.appendChild(a);
        a.click();
    };
    reader.readAsDataURL(file);
    evt.preventDefault();
    // window.open('data:application/json,' + exportFormat(allConnections));
});

document.querySelector('.reset-data').addEventListener('click', function(){
    addon.emit('reset');
    aggregate.emit('reset');
    currentVisualization.emit('reset');
    allConnections = [];
    delete localStorage.connections;
    delete localStorage.tempConnections;
    delete localStorage.totalNumConnections;
    // FIXME: empty the data from current view too
});

var uploadButton = document.querySelector('.upload');
if (localStorage.userHasOptedIntoSharing && localStorage.userHasOptedIntoSharing === 'true'){
    uploadButton.innerHTML = 'Stop Sharing';
}

uploadButton.addEventListener('click', function(){
    if (localStorage.userHasOptedIntoSharing && localStorage.userHasOptedIntoSharing === 'true'){
        stopSharing();
    }else{
        startSharing();
    }
});

document.querySelector('.disclosure').addEventListener('click', function(event){
    console.log('toggling filter open/closed');
    var filterSection = document.querySelector('.info .filters');
    filterSection.classList.toggle('closed');
});

function getZoom(canvas){
    // TODO: code cleanup if both cases use basically the same code
    switch(canvas){
        case 'vizcanvas': {
            var box = document.querySelector('.vizcanvas')
                        .getAttribute('viewBox')
                        .split(/\s/)
                        .map(function(i){ return parseInt(i, 10); });
            return {x: box[0], y: box[1], w: box[2], h: box[3]};
        }
        case 'mapcanvas': {
            var box = document.querySelector('.mapcanvas')
                        .getAttribute('viewBox')
                        .split(/\s/)
                        .map(function(i){ return parseInt(i, 10); });
            console.log(box);
            return {x: box[0], y: box[1], w: box[2], h: box[3]};
        }
        default: throw new Error('It has to be one of the choices above');
    }
}

function setZoom(box,canvas){
    // TODO: code cleanup if both cases use basically the same code
    switch(canvas){
        case 'vizcanvas': {
                document.querySelector('.vizcanvas')
                    .setAttribute('viewBox', [box.x, box.y, box.w, box.h].join(' '));
                break;
        }
        case 'mapcanvas': {
                document.querySelector('.mapcanvas')
                    .setAttribute('viewBox', [box.x, box.y, box.w, box.h].join(' '));
                break;

        }
        default: throw new Error('It has to be one of the choices above');
    }
}

/* Scroll over visualization to zoom in/out ========================= */

/* define viewBox limits
*  graph view default viewBox = " 0 0 1000 1000 "
*  clock                      = " -350 -495 700 500 "
*  map                        = " 0 0 2711.3 1196.7 "
*/
var graphZoomInLimit   = { x:300, y:300, w:200, h:300 };
var graphZoomOutLimit  = { w:4000, h:4000 };
var clockZoomInLimit   = { w:560, h:400 };
var clockZoomOutLimit  = { w:2800, h:2800 };
var mapZoomInLimit     = { w:(2711.3/5), h:(1196.7/5) };
var mapZoomOutLimit    = { w:2711.3, h:1196.7 };

document.querySelector(".stage").addEventListener("wheel",function(event){
    if ( event.target.mozMatchesSelector(".vizcanvas, .vizcanvas *") && currentVisualization.name != "list" ){
        if ( currentVisualization.name == "graph" ){
            zoomWithinLimit(event,"vizcanvas", graphZoomInLimit, graphZoomOutLimit);
        }else{ // clock view
            zoomWithinLimit(event,"vizcanvas", clockZoomInLimit, clockZoomOutLimit);
        }
    }
},false);

document.querySelector(".world-map").addEventListener("wheel",function(event){
    if ( event.target.mozMatchesSelector(".mapcanvas, .mapcanvas *") ){
        zoomWithinLimit(event,"mapcanvas", mapZoomInLimit, mapZoomOutLimit );
    }
},false);

// Check to see if the viewBox of the targeting svg is within the limit we define
// if yes, zoom
function zoomWithinLimit(event, targetSvg, zoomInLimit, zoomOutLimit){
    var currentViewBox = getZoom(targetSvg);

    var withinZoomInLimit = ( currentViewBox.w > zoomInLimit.w && currentViewBox.h > zoomInLimit.h);
    if ( zoomInLimit.x && zoomInLimit.y ){
        withinZoomInLimit =
            withinZoomInLimit && ( currentViewBox.x < zoomInLimit.x && currentViewBox.y < zoomInLimit.y );
    }

    var withinZoomOutLimit = ( currentViewBox.w <= zoomOutLimit.w && currentViewBox.h <= zoomOutLimit.h );

    // event.deltaY can only be larger than 1.0 or less than -1.0
    // conditions set to +/- 3 to lower the scrolling control sensitivity
    if ( event.deltaY >= 3 && withinZoomOutLimit ){ // scroll up to zoom out
        svgZooming(targetSvg, (1/1.25));
    }
    if ( event.deltaY <= -3 && withinZoomInLimit) { // scroll down to zoom in
        svgZooming(targetSvg, 1.25);
    }
}

// Apply zoom level
function svgZooming(target,ratio){

    function generateNewViewBox(target, box){
        var oldWidth = box.w;
        var newWidth = oldWidth / ratio;
        var offsetX = ( newWidth - oldWidth ) / 2;

        var oldHeight = box.h;
        var newHeight = oldHeight / ratio;
        var offsetY = ( newHeight - oldHeight ) / 2;

        box.w = box.w / ratio;
        box.h = box.h / ratio;
        box.x = box.x - offsetX;

        if ( target == "vizcanvas" ){
            box.y = ( currentVisualization.name == "graph") ? (box.y - offsetY) : -1 * (box.h - 5);
        }else{
            box.y = box.y - offsetY;
        }

        return box;
    }

    if ( target == "vizcanvas" ){
        var box = getZoom("vizcanvas");
        var newViewBox = generateNewViewBox(target, box);
        setZoom(newViewBox,"vizcanvas");

    }else{
        var box = getZoom("mapcanvas");
        var newViewBox = generateNewViewBox(target, box);
        setZoom(newViewBox,"mapcanvas");
    }

}

/* Pan by dragging ======================================== */

var onDragGraph = false;
var onDragMap = false;
var graphDragStart = {};
var mapDragStart = {};

/* vizcanvas */
document.querySelector(".stage").addEventListener("mousedown",function(event){
    if ( event.target.mozMatchesSelector(".vizcanvas, .vizcanvas *") && !event.target.mozMatchesSelector(".node, .node *") ){
        onDragGraph = true;
        graphDragStart.x = event.clientX;
        graphDragStart.y = event.clientY;
    }

},false);

document.querySelector(".stage").addEventListener("mousemove",function(event){
    if ( event.target.mozMatchesSelector(".vizcanvas") && !event.target.mozMatchesSelector(".node, .node *") && onDragGraph ){
        document.querySelector(".vizcanvas").style.cursor = "-moz-grab";
        var offsetX = ( Math.ceil(event.clientX) - graphDragStart.x );
        var offsetY = ( Math.ceil(event.clientY) - graphDragStart.y );
        var box = getZoom("vizcanvas");
        box.x -= ( offsetX * box.w/700);
        box.y -= ( offsetY * box.h/700);
        graphDragStart.x += offsetX;
        graphDragStart.y += offsetY;
        setZoom(box,"vizcanvas");
    }

},false);

document.querySelector(".stage").addEventListener("mouseup",function(event){
    onDragGraph = false;
    document.querySelector(".vizcanvas").style.cursor = "default";
},false);

document.querySelector(".stage").addEventListener("mouseleave",function(event){
    onDragGraph = false;
    document.querySelector(".vizcanvas").style.cursor = "default";
},false);

/* mapcanvas */
document.querySelector(".world-map").addEventListener("mousedown",function(event){
    if ( event.target.mozMatchesSelector(".mapcanvas, .mapcanvas *") ){
        onDragMap = true;
        mapDragStart.x = event.clientX;
        mapDragStart.y = event.clientY;
    }
},false);

document.querySelector(".world-map").addEventListener("mousemove",function(event){
    if ( event.target.mozMatchesSelector(".mapcanvas, .mapcanvas *") && onDragMap ){
        document.querySelector(".mapcanvas").style.cursor = "-moz-grab";
        var offsetX = ( Math.ceil(event.clientX) - mapDragStart.x );
        var offsetY = ( Math.ceil(event.clientY) - mapDragStart.y );
        var box = getZoom("mapcanvas");
        box.x -= (offsetX * 10);
        box.y -= (offsetY * 10);
        mapDragStart.x += offsetX;
        mapDragStart.y += offsetY;
        setZoom(box,"mapcanvas");
    }

},false);

document.querySelector(".world-map").addEventListener("mouseup",function(event){
    onDragMap = false;
    document.querySelector(".mapcanvas").style.cursor = "default";
},false);

document.querySelector(".world-map").addEventListener("mouseleave",function(event){
    onDragMap = false;
    document.querySelector(".mapcanvas").style.cursor = "default";
},false);

/* Help Mode ========================= */
document.querySelector(".help-mode").checked = false;
document.querySelector(".help-mode").addEventListener("click", function(){
    if( this.checked ){
        triggerHelp(document.querySelector("body"), "toggleOnHelp", currentVisualization.name);
    }else{
        triggerHelp(document.querySelector("body"), "toggleOffHelp", currentVisualization.name);
    }
});

/* Settings Page ========================= */
document.querySelector(".settings").addEventListener("click", function(event){
    if ( currentVisualization.name == "clock" || currentVisualization.name == "graph" ){
        document.querySelector(".vizcanvas").classList.toggle("hide");
    }else{
        document.querySelector(".list-breadcrumb").classList.toggle("hide");
        document.querySelector(".list-header").classList.toggle("hide");
        document.querySelector(".list-table").classList.toggle("hide");

    }
    var infoBarVisible = document.querySelector("#content").classList.contains("showinfo");
    if ( infoBarVisible ){
        document.querySelector("#content").classList.remove("showinfo");
    }
    document.querySelector(".settings-page").classList.toggle("hide");
});

document.querySelector(".settings-page").addEventListener("click", function(event){
    if (event.target.mozMatchesSelector(".settings-page ul li, .settings-page ul li *")){
        var site = event.target;
        while(site.mozMatchesSelector(".settings-page ul li *")){
            site = site.parentElement;
        }
        site.querySelector(".settings-option").classList.toggle("hide");
        site.querySelector(".icon-caret-right").parentElement.classList.toggle("hide");
        site.querySelector(".icon-caret-down").parentElement.classList.toggle("hide");
    }
},false);

/* Clock View ===================================== */

function highlightColludedNode(selection){
    selection.each(function(){
        var colludedNode = d3.select(this);
        if ( colludedNode.classed("source") ){  // this instance of colluded node is a source node
            colludedNode.classed("colluded-source", true);
        }
        if ( colludedNode.classed("target") ){ // this instance of colluded node is a target node
            colludedNode.classed("colluded-target", true);
        }
    });
}

function applyHighlightingEffect(clickedNodeName){
    // reset styling effect
    d3.selectAll("g.node").classed("clicked-node", false)
                          .classed("colluded-source", false)
                          .classed("colluded-target", false);

    // highlight all instances of the clicked node(both source and target)
    d3.selectAll("g[data-name='" + clickedNodeName +"']")
            .classed("clicked-node", true);

    // find all the colluded sites and highlight all instances of them
    for ( var key in aggregate.nodeForKey( clickedNodeName ) ){
        if ( key != clickedNodeName ){
            d3.selectAll("g[data-name='"+ key +"']").call(highlightColludedNode);
        }
    }

}

document.querySelector('#content').addEventListener('click', function(event){
    /*
    *   When a node in the clock visualization is clicked,
    *       all instances of the same node across the day should be highlighted
    *       all colluded nodes should also be highlighted (differently)
    */
    if ( currentVisualization.name == "clock" ){
        // click could happen on .node or an element inside of .node
        if (event.target.mozMatchesSelector('.node, .node *')){
            var node = event.target;
            while(node.mozMatchesSelector('.node *')){
                node = node.parentElement;
            }
            applyHighlightingEffect(node.getAttribute("data-name"));
        }
    }
},false);

/* Export ========== */

function exportFormat(connections){
    return JSON.stringify({
        format: 'Collusion Save File',
        version: '1.1',
        token: localStorage.collusionToken,
        connections: excludePrivateConnection(connections)
    });
}

/* Filter out connections collected in Private Mode */
function excludePrivateConnection(connections){
    return connections.filter(function(connection){
        return (connection[FROM_PRIVATE_MODE] == null);
    })
}

/* Info Panel Connections List ===================================== */

document.querySelector(".connections-list ul").addEventListener("click", function(event){
    if (event.target.mozMatchesSelector("li")){
        if ( currentVisualization.name === "clock" ){
            applyHighlightingEffect(event.target.innerHTML);
        }
        else if ( currentVisualization.name === "list" ){
            currentVisualization.emit("showFilteredTable", event.target.innerHTML);
        }else{

        }
    }
});
<|endoftext|>


<commit_before>import React, {Component} from 'react'
import request from 'superagent'
import { default as Web3 } from 'web3'
import { default as contract} from 'truffle-contract'
import { Link } from 'react-router'
import AppBar from 'material-ui/AppBar'
import FlatButton from 'material-ui/FlatButton'
import IconButton from 'material-ui/IconButton'
import RaisedButton from 'material-ui/RaisedButton'
import {Table, TableBody, TableHeader, TableHeaderColumn, TableRow, TableRowColumn} from 'material-ui/Table'
import {Card, CardActions, CardHeader, CardMedia, CardTitle, CardText} from 'material-ui/Card'
import FloatingActionButton from 'material-ui/FloatingActionButton'
import ContentAdd from 'material-ui/svg-icons/content/add'
import Identicon from './Identicon'
import Divider from 'material-ui/Divider'
import Toggle from 'material-ui/Toggle'
import SideBar from './SideBar'
import GridCard from './GridCard'
import config from '../../config'
//import CourtBuyable from "../../build/contracts/BuyableCourt.json";

import '../www/styles/Kleroterion.scss'

class Dispute extends Component {

  state = {
  }

  render() {
    return (

        <AppBar
          title={<Link to='/' style={{fontFamily: 'code', letterSpacing: '10px', color: '#fff', textDecoration: 'none', position: 'relative', top: '-6px'}}>KLEROTERIONALPHA</Link>}
          showMenuIconButton= {false}
          iconElementRight={<Identicon seed='tesdt' />}
          style={{backgroundColor: 'rgba(0, 0, 0, 1)', color: 'rgba(255, 255, 255, 1)'}}
          iconStyleRight={{margin: '14px 0 0 0'}}
        />

          <SideBar />

Dispute

            <GridCard content={[
                {'title' : 'Akasha', 'img' : 'http://www.toprankblog.com/wp-content/uploads/people-focused-b2b-marketing.jpg'},
                {'title' : 'OpenBazzar', 'img' : 'http://www.toprankblog.com/wp-content/uploads/people-focused-b2b-marketing.jpg'},
                {'title' : 'Steemit', 'img' : 'http://www.toprankblog.com/wp-content/uploads/people-focused-b2b-marketing.jpg'}
              ]}
            />

KLEROTERION

Decentralized court project

    )
  }
}

export default Dispute
<commit_msg>refactoring in dump component<commit_after>import React, {Component} from 'react'
import request from 'superagent'
import { default as Web3 } from 'web3'
import { default as contract} from 'truffle-contract'
import { Link } from 'react-router'
import AppBar from 'material-ui/AppBar'
import FlatButton from 'material-ui/FlatButton'
import IconButton from 'material-ui/IconButton'
import RaisedButton from 'material-ui/RaisedButton'
import {Table, TableBody, TableHeader, TableHeaderColumn, TableRow, TableRowColumn} from 'material-ui/Table'
import {Card, CardActions, CardHeader, CardMedia, CardTitle, CardText} from 'material-ui/Card'
import FloatingActionButton from 'material-ui/FloatingActionButton'
import ContentAdd from 'material-ui/svg-icons/content/add'
import Identicon from './Identicon'
import Divider from 'material-ui/Divider'
import Toggle from 'material-ui/Toggle'
import SideBar from './SideBar'
import GridCard from './GridCard'
import config from '../../config'
//import CourtBuyable from "../../build/contracts/BuyableCourt.json";

import '../www/styles/Kleroterion.scss'

const Dispute = () => (

        <AppBar
          title={<Link to='/' style={{fontFamily: 'code', letterSpacing: '10px', color: '#fff', textDecoration: 'none', position: 'relative', top: '-6px'}}>KLEROTERIONALPHA</Link>}
          showMenuIconButton= {false}
          iconElementRight={<Identicon seed='tesdt' />}
          style={{backgroundColor: 'rgba(0, 0, 0, 1)', color: 'rgba(255, 255, 255, 1)'}}
          iconStyleRight={{margin: '14px 0 0 0'}}
        />

          <SideBar />

Dispute

            <GridCard content={[
                {'title' : 'Akasha', 'img' : 'http://www.toprankblog.com/wp-content/uploads/people-focused-b2b-marketing.jpg'},
                {'title' : 'OpenBazzar', 'img' : 'http://www.toprankblog.com/wp-content/uploads/people-focused-b2b-marketing.jpg'},
                {'title' : 'Steemit', 'img' : 'http://www.toprankblog.com/wp-content/uploads/people-focused-b2b-marketing.jpg'}
              ]}
            />

KLEROTERION

Decentralized court project

    )

export default Dispute
<|endoftext|>


<commit_before>(function () {
    'use strict';

    angular
        .module('eliteApp')
        .factory('eliteApi', ['$http', '$q', '$ionicLoading',  eliteApi])
    function eliteApi($http, $q, $ionicLoading) {
        var currentLeagueId;

        function getLeagues() {
            var deferred = $q.defer();
            $ionicLoading.show({template: 'Loading...'});
            $http
                .get("http://elite-schedule.net/api/leaguedata")
                .success(function(data) {
                    $ionicLoading.hide();
                    deferred.resolve(data);
                })
                .error(function() {
                    $ionicLoading.hide();
                    console.log("Error while making HTTP call.");
                    deferred.reject();
                });
            return deferred.promise;
        }

        function getLeagueData() {
            var deferred = $q.defer();
            $ionicLoading.show({template: 'Loading...'});
            $http
                .get("http://elite-schedule.net/api/leaguedata/" + currentLeagueId)
                .success(function(data, status) {
                    $ionicLoading.hide();
                    console.log("Received schedule data via HTTP.", data, status);
                    deferred.resolve(data);
                })
                .error(function() {
                    $ionicLoading.hide();
                    console.log("Error while making HTTP call.");
                    deferred.reject();
                });
            return deferred.promise;
        }

        function setLeagueId(leagueId) {
            currentLeagueId = leagueId;
        }

        return {
            getLeagues: getLeagues,
            getLeagueData: getLeagueData,
            setLeagueId: setLeagueId
        }
    }
})();
<commit_msg>implementing caches<commit_after>(function () {
    'use strict';

    angular
        .module('eliteApp')
        .factory('eliteApi', ['$http', '$q', '$ionicLoading', 'CacheFactory', eliteApi])
    function eliteApi($http, $q, $ionicLoading, CacheFactory) {
        var currentLeagueId;

        self.leaguesCache = CacheFactory.get("leaguesCache");
        self.leagueDataCache = CacheFactory.get("leagueDataCache");

        function getLeagues() {
            var deferred = $q.defer();
            var cacheKey = 'leagues';
            var leaguesData = self.leaguesCache.get(cacheKey);

            if(leaguesData) {
                console.log("Found data inside cache", leaguesData);
                deferred.resolve(leaguesData);
            } else {
                $ionicLoading.show({template: 'Loading...'});
                $http
                    .get("http://elite-schedule.net/api/leaguedata")
                    .success(function(data) {
                        $ionicLoading.hide();
                        console.log("Received data via HTTP");
                        self.leaguesCache.put(cacheKey, data);
                        deferred.resolve(data);
                    })
                    .error(function() {
                        $ionicLoading.hide();
                        console.log("Error while making HTTP call.");
                        deferred.reject();
                    });
            }

            return deferred.promise;
        }

        function getLeagueData() {
            var deferred = $q.defer();
            var cacheKey = 'leagueData-' + currentLeagueId;
            var leagueData = self.leagueDataCache.get(cacheKey);

            if(leagueData) {
                console.log("Found data in cache", leagueData);
                deferred.resolve(leagueData);
            } else {
                $ionicLoading.show({template: 'Loading...'});
                $http
                    .get("http://elite-schedule.net/api/leaguedata/" + currentLeagueId)
                    .success(function(data, status) {
                        $ionicLoading.hide();
                        console.log("Received schedule data via HTTP.", data, status);
                        self.leagueDataCache.put(cacheKey, data);
                        deferred.resolve(data);
                    })
                    .error(function() {
                        $ionicLoading.hide();
                        console.log("Error while making HTTP call.");
                        deferred.reject();
                    });
            }
            return deferred.promise;
        }

        function setLeagueId(leagueId) {
            currentLeagueId = leagueId;
        }

        return {
            getLeagues: getLeagues,
            getLeagueData: getLeagueData,
            setLeagueId: setLeagueId
        }
    }
})();
<|endoftext|>