<commit_before>/* Purpose: Check or fix scripts according Standard JS and create log file with remaining findings. */

'use strict'

// Load packages
let env = require('./env')
let alert = require('./alert')
let cmd = require('./cmd')
let fs = require('fs-extra')
let abs = require('path').resolve

// Show message
alert('Code ' + (env.arg.fix === true ? 'fix' : 'check') + ' ongoing - please wait ...')

// Define log file
let logFile = 'code-unconformities.log'

// Define Standard parameters
let params = [
  'node',
  'node_modules/eslint/bin/eslint.js',
  // Find app.vue, pages/*.vue and pages/sub/*.vue
  '"' + abs(env.app, '**/*.vue') + '"',
  '"' + abs(env.app, '**/*.js') + '"'
]
if (!env.installed) {
  params.push('"' + abs(__dirname, '../client/*.js') + '"')
  params.push('"' + abs(__dirname, '../scripts/*.js') + '"')
}
// Print to log file
params.push('>')
params.push('"' + abs(env.proj, logFile) + '"')
if (env.arg.fix === true) {
  params.push('--fix')
}

// Define error alert
let errorAlert = (env.arg.fix !== true ? 'Code unconformities found.' : 'Some unconformities must be fixed manually.') + '\n' +
                 'Please check "' + logFile + '" for detailed information.\n' +
                 (env.arg.fix !== true ? 'You can run "npm run fix" first for automatic fix.' : '')

// Update eslint config file
const eslintConfig = env.cfg.eslint
if (eslintConfig.extends === 'airbnb') {
  eslintConfig.extends = 'airbnb-base'
}
eslintConfig.plugins = ['vue']
try {
  fs.writeJsonSync(abs(env.proj, '.eslintrc'), eslintConfig, { spaces: 2 })
} catch (err) {
  alert('Failed to update the ESLint configuration file.', 'issue')
}

// Do the fix
cmd([env.proj], params, function () {
  fs.removeSync(abs(env.proj, logFile))
  alert('Code ' + (env.arg.fix === true ? 'fix' : 'check') + ' done without findings.')
}, function () {
  alert(errorAlert, 'error')
})
<commit_msg>add ESLint env: browser + node<commit_after>/* Purpose: Check or fix scripts according Standard JS and create log file with remaining findings. */

// Load packages
const env = require('./env')
const alert = require('./alert')
const cmd = require('./cmd')
const fs = require('fs-extra')
const abs = require('path').resolve

// Show message
alert(`Code ${env.arg.fix === true ? 'fix' : 'check'} ongoing - please wait ...`)

// Define log file
const logFile = 'code-unconformities.log'

// Define Standard parameters
const params = [
  'node',
  'node_modules/eslint/bin/eslint.js',
  // Find app.vue, pages/*.vue and pages/sub/*.vue
  `"${abs(env.app, '**/*.vue')}"`,
  `"${abs(env.app, '**/*.js')}"`
]
if (!env.installed) {
  params.push(`"${abs(__dirname, '../client/*.js')}"`)
  params.push(`"${abs(__dirname, '../scripts/*.js')}"`)
}
// Print to log file
params.push('>')
params.push(`"${abs(env.proj, logFile)}"`)
if (env.arg.fix === true) {
  params.push('--fix')
}

// Define error alert
const errorAlert = `${env.arg.fix !== true ? 'Code unconformities found.' : 'Some unconformities must be fixed manually.'}\n` +
                 `Please check "${logFile}" for detailed information.\n${
                   env.arg.fix !== true ? 'You can run "npm run fix" first for automatic fix.' : ''}`

// Update eslint config file
const eslintConfig = env.cfg.eslint
if (eslintConfig.extends === 'airbnb') {
  eslintConfig.extends = 'airbnb-base'
}
eslintConfig.plugins = ['vue']
eslintConfig.env = {
  browser: true,
  node: true
}
try {
  fs.writeJsonSync(abs(env.proj, '.eslintrc'), eslintConfig, { spaces: 2 })
} catch (err) {
  alert('Failed to update the ESLint configuration file.', 'issue')
}

// Do the fix
cmd([env.proj], params, () => {
  fs.removeSync(abs(env.proj, logFile))
  alert(`Code ${env.arg.fix === true ? 'fix' : 'check'} done without findings.`)
}, () => {
  alert(errorAlert, 'error')
})
<|endoftext|>


<commit_before>import Morph from "./Morph.js"

import d3 from 'src/external/d3.v4.js';

import { Graph } from 'src/client/triples/triples.js';
import * as drawTools from 'src/client/triples/drawTools.js';
import * as math from 'src/client/triples/math.js';

const MIN_MAGNIFICATION = 0.01;
const MAX_MAGNIFICATION = 4;

const NODE_BY_KNOT = new Map();
function getNodeByKnot(knot) {
  if(!NODE_BY_KNOT.has(knot)) {
    NODE_BY_KNOT.set(knot, new Node(knot));
  }
  return NODE_BY_KNOT.get(knot);
}

class RectangleTools {
  static distanceToBorder(rect, dx, dy) {
  	var width = rect.width(),
  		height = rect.height();

  	var innerDistance,
  		m_link = Math.abs(dy / dx),
  		m_rect = height / width;

  	if (m_link <= m_rect) {
  		var timesX = dx / (width / 2),
  			rectY = dy / timesX;
  		innerDistance = Math.sqrt(Math.pow(width / 2, 2) + Math.pow(rectY, 2));
  	} else {
  		var timesY = dy / (height / 2),
  			rectX = dx / timesY;
  		innerDistance = Math.sqrt(Math.pow(height / 2, 2) + Math.pow(rectX, 2));
  	}

  	return innerDistance;
  };
}

const cssClassesByFileType = {
  'png': ['image'],
  'PNG': ['image'],
  'jpg': ['image'],
  'svg': ['image'],
  'mp3': ['audio'],
  'mov': ['movie'],
  'mp4': ['movie'],
  'avi': ['movie'],
  'pptx': ['presentation'],
  'md': ['markdown'],
  'htm': ['html'],
  'html': ['html'],
  'json': ['data'],
  'csv': ['data']
}
const cssClassesByTagURL = {
  'https://lively4/dropbox/tag.md': ['tag'],
  'https://lively4/dropbox/is_a.md': ['is-a'],
  'https://lively4/dropbox/same_as.md': ['same-as'],
  'https://lively4/dropbox/contains.md': ['contains'],
}
class Node {
  static getCSSClassesByType(node) {

  }
  constructor(knot, label) {
    this.knot = knot;
    //this.r = ~~d3.randomUniform(8, 28)();
    const fileEnding = this.getFileEnding();
    const shouldRenderSizeAware = (fileEnding === 'md' || fileEnding === 'html') &&
     this.knot.content && this.knot.content.split;
    this._radius = shouldRenderSizeAware ?
      Math.min((this.knot.content.split(/\r?\n/).length + 10) * 0.9, 100) :
	    40;
	 this._width = 60;
	 this._height = 20;
  }
  getKnot() { return this.knot; }
  label() { return this.knot.label(); }
  isTriple() { return this.knot.isTriple(); }
  isExternal() {  return Graph.isExternalURL(new URL(this.knot.url)); }
  getFileEnding() { return this.knot.url.split('#').shift().split('?').shift().split('.').pop(); }

  draw(parentElement, additionalCssClasses) {
    let cssClasses;

    if(this.knot.isTriple()) {
      cssClasses = cssClassesByTagURL[this.knot.predicate.url] || ['unspecified'];
      cssClasses.push('nostroke')
    } else {
      const fileEnding = this.getFileEnding();
      //lively.notify(fileEnding)
  		cssClasses = cssClassesByFileType[fileEnding] ?
  		  cssClassesByFileType[fileEnding] :
  		  (lively.notify(fileEnding), ['white']);
    }

    if(this.knot.isTriple()) {
      drawTools.appendRectangularClass(parentElement, 60, 20, cssClasses, this.label());
    } else {
		  drawTools.appendCircularClass(parentElement, this.actualRadius(), cssClasses, this.label());
    }

		//that.postDrawActions(parentElement);
	}

	actualRadius() {
	  return this._radius;
	}
	width() { return this._width; }
	height() { return this._height; }
	distanceToBorder(dx, dy) { return this.isTriple() ?
	  RectangleTools.distanceToBorder(this, dx, dy) :
	  this.actualRadius();
	}
}

class Link extends Node {
  constructor(node) {
    super(node.getKnot());

    this._subject = getNodeByKnot(this.getKnot().subject);
    this._predicate = getNodeByKnot(this.getKnot().predicate);
    this._object = getNodeByKnot(this.getKnot().object);
    this._triple = getNodeByKnot(this.getKnot());

    this.frontPart = new LinkPart({
      source: this._subject,
      target: this._triple
    });
    this.backPart = new LinkPart({
      source: this._triple,
      target: this._object
    });
  }
  get subject() { return this._subject; }
  get predicate() { return this._predicate; }
  get object() { return this._object; }
  get triple() { return this._triple; }

  //TODO: support loops specially
  isLoop() {
    return this.subject === this.object;
  }
  linkParts() {
    return [this.frontPart, this.backPart];
  }

  draw(linkGroup, markerContainer) {
  	linkGroup.append("path")
  		.classed("link-path", true)
  		//.classed(this.domain().cssClassOfNode(), true)
  		//.classed(this.range().cssClassOfNode(), true)
      //.classed(property.linkType(), true);
  }

  // TODO: if we are self-referencial: compute the number of self references from the graph
  loops() {
    return 3;
  }
}

class LinkPart {
  constructor({ source, target }) {
    this.source = source;
    this.target = target;
  }
}

export default class TripleNotes extends Morph {

  async initialize() {
    this.windowTitle = "Knot Explorer";

    let parentElement = this.get('#graph');
    var width,height;
    var chartWidth, chartHeight;
    var margin;
    var svg = d3.select(parentElement)
      .append("svg");
    var graphContainer = svg.append("g").classed("graphContainer", true);

    setSize();

    let graph = await Graph.getInstance();

    let knots = graph.knots;
    this.updateStatistics(knots);

    let nodes = knots.map(getNodeByKnot);
    let links = nodes
      .filter(node => node.isTriple())
      .map(node => new Link(node));

    let hiddenLinks = [];
		links.forEach(link => hiddenLinks = hiddenLinks.concat(link.linkParts()));

    drawChart.call(this, {
      nodes,
      links,
      hiddenLinks
    });

    lively.addEventListener("triple-notes", this, "extent-changed", e => { setSize(); });

    function setSize() {
      width = parentElement.clientWidth;
      height = parentElement.clientHeight;

      margin = {top:0, left:0, bottom:0, right:0 };

      chartWidth = width - (margin.left+margin.right)
      chartHeight = height - (margin.top+margin.bottom)

      svg
        .attr("width", chartWidth)
        .attr("height", chartHeight)
        .attr("transform", "translate("+[margin.left, margin.top]+")");
    }

    function zoomed() {
      graphContainer.attr("transform", d3.event.transform);
    }

    svg.call(d3.zoom()
			.duration(150)
    	.scaleExtent([MIN_MAGNIFICATION, MAX_MAGNIFICATION])
      .on("zoom", zoomed));

    function drawChart({ nodes, links, hiddenLinks }) {

      // hidden link parts
      let linkPartContainer = graphContainer.append("g").classed("linkPartContainer", true);
      var hiddenLinkElements = linkPartContainer.selectAll("line")
        .data(hiddenLinks).enter()
        .append("line")
        .style("stroke", "black")
        .style("stroke-opacity", 0.1)
        .style("stroke-width", 10);

      // Curved, visible  Links
      let linkContainer = graphContainer.append("g").classed("linkContainer", true);
      let markerContainer = linkContainer.append("defs");
      createPropertyMarker(markerContainer);
      function createPropertyMarker(markerContainer) {
      	var marker = appendBasicMarker(markerContainer);
      	//marker.attr("refX", 12);
          var m1X = -12 ;
          var m1Y = 8 ;
          var m2X = -12;
          var m2Y = -8 ;
      	marker.append("path")
      		//.attr("d", "M0,-8L12,0L0,8Z")
              .attr("d", "M0,0L " + m1X + "," + m1Y + "L" + m2X + "," + m2Y + "L" + 0 + "," + 0 )
      		.classed("basic-triple-link", true);
      }

      function appendBasicMarker(markerContainer) {
      	return markerContainer.append("marker")
      		.datum({})
      		.attr("id", "triple-notes-basic-triple-link")

      		.attr("viewBox", "-14 -10 28 20")
      		.attr("markerWidth",10)
      		.attr("markerHeight", 10)
      		//.attr("markerUnits", "userSpaceOnUse")
      		.attr("orient", "auto");
      }

  		// Draw links
  		let linkGroups = linkContainer.selectAll(".link")
  			.data(links).enter()
  			.append("g")
  			.classed("link", true);

  		linkGroups.each(function (link) {
  		  d3.select(this).attr("marker-end", "url(#triple-notes-basic-triple-link)");
  			link.draw(d3.select(this), markerContainer);
  		});

  		// Select the path for direct access to receive a better performance
      let linkPathElements = linkGroups.selectAll("path");

      let curveFunction = d3.line()
  			.x(function (d) {
  				return d.x;
  			})
  			.y(function (d) {
  				return d.y;
  			})
        .curve(d3.curveNatural);

      // Visible Knots -> Nodes
      let nodeContainer = graphContainer.append("g").classed("nodeContainer", true);
      let nodeElements = nodeContainer.selectAll(".node")
        .data(nodes).enter()
        .append("g")
        .attr("class", "node")
        .call(d3.drag()
          .on("start", dragstarted)
          .on("drag", dragged)
          .on("end", dragended))
          .on("dblclick", async node => {
            d3.event.stopPropagation();

            let knotView = await lively.openComponentInWindow("knot-view");
            knotView.loadKnotForURL(node.getKnot().url);
          });

      nodeElements.each(function (node) {
  			node.draw(d3.select(this));
      });

      nodeElements.append("text")
        .attr("class", "text")
        .style("text-anchor", "middle")
        .style("alignment-baseline", "middle")
        .text(d => d.label());

      var simulation = d3.forceSimulation()
        .force("link", d3.forceLink().id(d => d.index).distance(200))
        //.force("collide",d3.forceCollide(d => d.r + 8).iterations(16) )
        .force("charge", d3.forceManyBody().strength(node => node.isTriple() ? -190*0.5 : -190))
        .force("center", d3.forceCenter(chartWidth / 2, chartWidth / 2))
        .force("y", d3.forceY(0).strength(0.001))
        .force("x", d3.forceX(0).strength(0.001));

      // Define Simulation
      simulation
        .nodes(nodes)
        .on("tick", function recalculatePositions() {
          nodeElements.attr("transform", d => "translate(" + d.x + "," + d.y + ")");

          hiddenLinkElements
            .attr("x1", d => d.source.x)
            .attr("y1", d => d.source.y)
            .attr("x2", d => d.target.x)
            .attr("y2", d => d.target.y);

      		// Set link paths and calculate additional information
      		linkPathElements.attr("d", function (link) {
      			if (link.isLoop()) {
      				return math.calculateLoopPath(link);
      			}
      			var curvePoint = link.triple;
      			var pathStart = math.calculateIntersection(curvePoint, link.subject, 1);
      			var pathEnd = math.calculateIntersection(curvePoint, link.object, 1);

      			return curveFunction([pathStart, curvePoint, pathEnd]);
          });
        });

      simulation.force("link")
        .links(hiddenLinks);

      function dragstarted(d) {
        if (!d3.event.active) simulation.alphaTarget(0.3).restart();
        d.fx = d.x;
        d.fy = d.y;
      }

      function dragged(d) {
        d.fx = d3.event.x;
        d.fy = d3.event.y;
      }

      function dragended(d) {
        if (!d3.event.active) simulation.alphaTarget(0);
        d.fx = null;
        d.fy = null;
      }

      var linkDistance = this.get('#link-distance');
      linkDistance.addEventListener('input', () => {
        lively.notify(`New link distance is ${linkDistance.value}`);
        simulation.force("link").distance(linkDistance.value);
        simulation.alpha(1).restart();
      });
    }
  }

  updateStatistics(knots) {
    this.get('#number-of-knots').innerHTML = knots.length;
    this.get('#number-of-triples').innerHTML = knots.filter(k => k.isTriple()).length;
  }
}<commit_msg>AUTO-COMMIT templates/triple-notes.js<commit_after>import Morph from "./Morph.js"

import d3 from 'src/external/d3.v4.js';

import { Graph } from 'src/client/triples/triples.js';
import * as drawTools from 'src/client/triples/drawTools.js';
import * as math from 'src/client/triples/math.js';

const MIN_MAGNIFICATION = 0.01;
const MAX_MAGNIFICATION = 4;

const NODE_BY_KNOT = new Map();
function getNodeByKnot(knot) {
  if(!NODE_BY_KNOT.has(knot)) {
    NODE_BY_KNOT.set(knot, new Node(knot));
  }
  return NODE_BY_KNOT.get(knot);
}

class RectangleTools {
  static distanceToBorder(rect, dx, dy) {
  	var width = rect.width(),
  		height = rect.height();

  	var innerDistance,
  		m_link = Math.abs(dy / dx),
  		m_rect = height / width;

  	if (m_link <= m_rect) {
  		var timesX = dx / (width / 2),
  			rectY = dy / timesX;
  		innerDistance = Math.sqrt(Math.pow(width / 2, 2) + Math.pow(rectY, 2));
  	} else {
  		var timesY = dy / (height / 2),
  			rectX = dx / timesY;
  		innerDistance = Math.sqrt(Math.pow(height / 2, 2) + Math.pow(rectX, 2));
  	}

  	return innerDistance;
  };
}

const cssClassesByFileType = {
  'png': ['image'],
  'PNG': ['image'],
  'jpg': ['image'],
  'svg': ['image'],
  'mp3': ['audio'],
  'mov': ['movie'],
  'mp4': ['movie'],
  'avi': ['movie'],
  'pptx': ['presentation'],
  'md': ['markdown'],
  'htm': ['html'],
  'html': ['html'],
  'json': ['data'],
  'csv': ['data']
}
const cssClassesByTagURL = {
  'https://lively4/dropbox/tag.md': ['tag'],
  'https://lively4/dropbox/is_a.md': ['is-a'],
  'https://lively4/dropbox/same_as.md': ['same-as'],
  'https://lively4/dropbox/contains.md': ['contains'],
}
class Node {
  static getCSSClassesByType(node) {

  }
  constructor(knot, label) {
    this.knot = knot;
    //this.r = ~~d3.randomUniform(8, 28)();
    const fileEnding = this.getFileEnding();
    const shouldRenderSizeAware = (fileEnding === 'md' || fileEnding === 'html') &&
     this.knot.content && this.knot.content.split;
    this._radius = shouldRenderSizeAware ?
      Math.min((this.knot.content.split(/\r?\n/).length + 10) * 0.9, 100) :
	    40;
	 this._width = 60;
	 this._height = 20;
  }
  getKnot() { return this.knot; }
  label() { return this.knot.label(); }
  isTriple() { return this.knot.isTriple(); }
  isExternal() {  return Graph.isExternalURL(new URL(this.knot.url)); }
  getFileEnding() { return this.knot.url.split('#').shift().split('?').shift().split('.').pop(); }

  draw(parentElement, additionalCssClasses) {
    let cssClasses;

    if(this.knot.isTriple()) {
      cssClasses = cssClassesByTagURL[this.knot.predicate.url] || ['unspecified'];
      cssClasses.push('nostroke')
    } else {
      const fileEnding = this.getFileEnding();
      //lively.notify(fileEnding)
  		cssClasses = cssClassesByFileType[fileEnding] ?
  		  cssClassesByFileType[fileEnding] :
  		  (lively.notify(fileEnding), ['white']);
    }

    if(this.knot.isTriple()) {
      drawTools.appendRectangularClass(parentElement, 60, 20, cssClasses, this.label());
    } else {
		  drawTools.appendCircularClass(parentElement, this.actualRadius(), cssClasses, this.label());
    }

		//that.postDrawActions(parentElement);
	}

	actualRadius() {
	  return this._radius;
	}
	width() { return this._width; }
	height() { return this._height; }
	distanceToBorder(dx, dy) { return this.isTriple() ?
	  RectangleTools.distanceToBorder(this, dx, dy) :
	  this.actualRadius();
	}
}

class Link extends Node {
  constructor(node) {
    super(node.getKnot());

    this._subject = getNodeByKnot(this.getKnot().subject);
    this._predicate = getNodeByKnot(this.getKnot().predicate);
    this._object = getNodeByKnot(this.getKnot().object);
    this._triple = getNodeByKnot(this.getKnot());

    this.frontPart = new LinkPart({
      source: this._subject,
      target: this._triple
    });
    this.backPart = new LinkPart({
      source: this._triple,
      target: this._object
    });
  }
  get subject() { return this._subject; }
  get predicate() { return this._predicate; }
  get object() { return this._object; }
  get triple() { return this._triple; }

  //TODO: support loops specially
  isLoop() {
    return this.subject === this.object;
  }
  linkParts() {
    return [this.frontPart, this.backPart];
  }

  draw(linkGroup, markerContainer) {
  	linkGroup.append("path")
  		.classed("link-path", true)
  		//.classed(this.domain().cssClassOfNode(), true)
  		//.classed(this.range().cssClassOfNode(), true)
      //.classed(property.linkType(), true);
  }

  // TODO: if we are self-referencial: compute the number of self references from the graph
  loops() {
    return 3;
  }
}

class LinkPart {
  constructor({ source, target }) {
    this.source = source;
    this.target = target;
  }
}

export default class TripleNotes extends Morph {

  async initialize() {
    this.windowTitle = "Knot Explorer";

    let parentElement = this.get('#graph');
    var width,height;
    var chartWidth, chartHeight;
    var margin;
    var svg = d3.select(parentElement)
      .append("svg");
    var graphContainer = svg.append("g").classed("graphContainer", true);

    setSize();

    let graph = await Graph.getInstance();

    let knots = graph.knots;
    this.updateStatistics(knots);

    let nodes = knots.map(getNodeByKnot);
    let links = nodes
      .filter(node => node.isTriple())
      .map(node => new Link(node));

    let hiddenLinks = [];
		links.forEach(link => hiddenLinks = hiddenLinks.concat(link.linkParts()));

    drawChart.call(this, {
      nodes,
      links,
      hiddenLinks
    });

    lively.addEventListener("triple-notes", this, "extent-changed", e => { setSize(); });

    function setSize() {
      width = parentElement.clientWidth;
      height = parentElement.clientHeight;

      margin = {top:0, left:0, bottom:0, right:0 };

      chartWidth = width - (margin.left+margin.right)
      chartHeight = height - (margin.top+margin.bottom)

      svg
        .attr("width", chartWidth)
        .attr("height", chartHeight)
        .attr("transform", "translate("+[margin.left, margin.top]+")");
    }

    function zoomed() {
      graphContainer.attr("transform", d3.event.transform);
    }

    svg.call(d3.zoom()
			.duration(150)
    	.scaleExtent([MIN_MAGNIFICATION, MAX_MAGNIFICATION])
      .on("zoom", zoomed));

    function drawChart({ nodes, links, hiddenLinks }) {

      // hidden link parts
      let linkPartContainer = graphContainer.append("g").classed("linkPartContainer", true);
      var hiddenLinkElements = linkPartContainer.selectAll("line")
        .data(hiddenLinks).enter()
        .append("line")
        .style("stroke", "black")
        .style("stroke-opacity", 0.1)
        .style("stroke-width", 10);

      // Curved, visible  Links
      let linkContainer = graphContainer.append("g").classed("linkContainer", true);
      let markerContainer = linkContainer.append("defs");
      createPropertyMarker(markerContainer);
      function createPropertyMarker(markerContainer) {
      	var marker = appendBasicMarker(markerContainer);
      	//marker.attr("refX", 12);
          var m1X = -12 ;
          var m1Y = 8 ;
          var m2X = -12;
          var m2Y = -8 ;
      	marker.append("path")
      		//.attr("d", "M0,-8L12,0L0,8Z")
              .attr("d", "M0,0L " + m1X + "," + m1Y + "L" + m2X + "," + m2Y + "L" + 0 + "," + 0 )
      		.classed("basic-triple-link", true);
      }

      function appendBasicMarker(markerContainer) {
      	return markerContainer.append("marker")
      		.datum({})
      		.attr("id", "triple-notes-basic-triple-link")

      		.attr("viewBox", "-14 -10 28 20")
      		.attr("markerWidth",10)
      		.attr("markerHeight", 10)
      		//.attr("markerUnits", "userSpaceOnUse")
      		.attr("orient", "auto");
      }

  		// Draw links
  		let linkGroups = linkContainer.selectAll(".link")
  			.data(links).enter()
  			.append("g")
  			.classed("link", true);

  		linkGroups.each(function (link) {
  		  d3.select(this).attr("marker-end", "url(#triple-notes-basic-triple-link)");
  			link.draw(d3.select(this), markerContainer);
  		});

  		// Select the path for direct access to receive a better performance
      let linkPathElements = linkGroups.selectAll("path");

      let curveFunction = d3.line()
  			.x(function (d) {
  				return d.x;
  			})
  			.y(function (d) {
  				return d.y;
  			})
        .curve(d3.curveNatural);

      // Visible Knots -> Nodes
      let nodeContainer = graphContainer.append("g").classed("nodeContainer", true);
      let nodeElements = nodeContainer.selectAll(".node")
        .data(nodes).enter()
        .append("g")
        .attr("class", "node")
        .call(d3.drag()
          .on("start", dragstarted)
          .on("drag", dragged)
          .on("end", dragended))
          .on("dblclick", async node => {
            d3.event.stopPropagation();

            let knotView = await lively.openComponentInWindow("knot-view");
            knotView.loadKnotForURL(node.getKnot().url);
          });

      nodeElements.each(function (node) {
  			node.draw(d3.select(this));
      });

      nodeElements.append("text")
        .attr("class", "text")
        .style("text-anchor", "middle")
        .style("alignment-baseline", "middle")
        .text(d => d.label());

      var simulation = d3.forceSimulation()
        .force("link", d3.forceLink().id(d => d.index).distance(200))
        .force("collide",d3.forceCollide(d => d.r + 8).iterations(16) )
        .force("charge", d3.forceManyBody().strength(node => node.isTriple() ? -190*0.5 : -190))
        .force("center", d3.forceCenter(chartWidth / 2, chartWidth / 2))
        .force("y", d3.forceY(0).strength(0.001))
        .force("x", d3.forceX(0).strength(0.001));

      // Define Simulation
      simulation
        .nodes(nodes)
        .on("tick", function recalculatePositions() {
          nodeElements.attr("transform", d => "translate(" + d.x + "," + d.y + ")");

          hiddenLinkElements
            .attr("x1", d => d.source.x)
            .attr("y1", d => d.source.y)
            .attr("x2", d => d.target.x)
            .attr("y2", d => d.target.y);

      		// Set link paths and calculate additional information
      		linkPathElements.attr("d", function (link) {
      			if (link.isLoop()) {
      				return math.calculateLoopPath(link);
      			}
      			var curvePoint = link.triple;
      			var pathStart = math.calculateIntersection(curvePoint, link.subject, 1);
      			var pathEnd = math.calculateIntersection(curvePoint, link.object, 1);

      			return curveFunction([pathStart, curvePoint, pathEnd]);
          });
        });

      simulation.force("link")
        .links(hiddenLinks);

      function dragstarted(d) {
        if (!d3.event.active) simulation.alphaTarget(0.3).restart();
        d.fx = d.x;
        d.fy = d.y;
      }

      function dragged(d) {
        d.fx = d3.event.x;
        d.fy = d3.event.y;
      }

      function dragended(d) {
        if (!d3.event.active) simulation.alphaTarget(0);
        d.fx = null;
        d.fy = null;
      }

      var linkDistance = this.get('#link-distance');
      linkDistance.addEventListener('input', () => {
        lively.notify(`New link distance is ${linkDistance.value}`);
        simulation.force("link").distance(linkDistance.value);
        simulation.alpha(1).restart();
      });
    }
  }

  updateStatistics(knots) {
    this.get('#number-of-knots').innerHTML = knots.length;
    this.get('#number-of-triples').innerHTML = knots.filter(k => k.isTriple()).length;
  }
}<|endoftext|>


<commit_before>elation.require([], function() {
  /**
   * Geometry Generators
   */
  elation.extend("engine.geometries", new function() {
    this.generators = {
      /**
       * Takes an array of geometries and merges them into one
       * @param genargs {array}
       */
      'merge': function(genargs) {
        var geom = new THREE.Geometry();
        for (var i = 0; i < genargs.length; i++) {
          if (genargs[i]) {
            THREE.GeometryUtils.merge(geom, elation.engine.geometries.getmesh(genargs[i]));
          }
        }
        return geom;
      },
      /**
       * Flattens a THREE.Object3D and its object heirarchy into a single geometry
       * @param genargs {object}
       */
      'shrinkwrap': function(genargs) {
        var obj = genargs.obj;
        var geometry = genargs.geometry;
        if (typeof geometry == 'undefined') {
          geometry = new THREE.Geometry();
        }
        if (obj instanceof THREE.Mesh) {
          THREE.GeometryUtils.merge(geometry, obj);
        }
        if (obj.children.length > 0) {
          for (var i = 0; i < obj.children.length; i++) {
            elation.engine.geometries.generate('shrinkwrap', {obj: obj.children[i], geometry: geometry});
          }
        }
        return geometry;
      },
      /**
       * Repeats the specified geometry a number of times with a configurable offset
       * @param genargs {object}
       * @param genargs.geometry {THREE.Geometry|THREE.Mesh}
       * @param genargs.repeat {integer}
       * @param genargs.offset {float}
       * @param genargs.axis {THREE.Vector3}
       * @param genargs.relativeoffset {float}
       */
      'array': function(genargs) {
        var geom = new THREE.Geometry();
        if (genargs.geometry) {
          var base = elation.engine.geometries.getmesh(genargs.geometry);
          var axis = genargs.axis || new THREE.Vector3(1, 0, 0);
          axis.normalize();
          var repeat = genargs.repeat || 1;
          var offset = 0;
          if (genargs.offset) {
            offset = genargs.offset;
          } else if (genargs.relativeoffset) {
            // TODO - relative offset should get the bbox from the base geometry, project it onto the axis, and multiply by the provided scalar
          }
          for (var i = 0; i < repeat; i++) {
            base.position.copy(axis).multiplyScalar(i * offset);
            THREE.GeometryUtils.merge(geom, base);
          }
        }
        return geom;
      },
      'triangle': function(params) {
        var p1 = params.p1 || new THREE.Vector3(),
            p2 = params.p2 || new THREE.Vector3(),
            p3 = params.p3 || new THREE.Vector3();
        var geo = new THREE.BufferGeometry();
        var positions = new Float32Array(3 * 3);

        positions[0] = p1.x;
        positions[1] = p1.y;
        positions[2] = p1.z;

        positions[3] = p2.x;
        positions[4] = p2.y;
        positions[5] = p2.z;

        positions[6] = p3.x;
        positions[7] = p3.y;
        positions[8] = p3.z;

        geo.addAttribute('position', new THREE.BufferAttribute(positions, 3));
        geo.computeFaceNormals();
        geo.computeVertexNormals();

        return geo;
      },
      'sphere': function(params) {
        var radius = params.radius,
            widthSegments = params.heightSegments || 8,
            heightSegments = params.widthSegments || 6,
            phiStart = params.phiStart || 0,
            phiLength = params.phiLength || Math.PI * 2,
            thetaStart = params.thetaStart || 0,
            thetaLength = params.thetaLength || Math.PI;
        var geo = new THREE.SphereBufferGeometry(radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength);
        return geo;
      },
      'box': function(params) {
        var size = params.size || new THREE.Vector3(1,1,1),
            offset = params.offset;

        var geo = new THREE.BoxBufferGeometry(size.x, size.y, size.z, 1, 1, 1);

        if (offset) {
          geo.applyMatrix4(new THREE.Matrix4().makeTranslation(offset.x, offset.y, offset.z));
        }
        return geo;
      },
      'cylinder': function(params) {
        var radius = params.radius,
            height = params.height,
            radialSegments = params.radialSegments || 8,
            heightSegments = params.heightSegments || 1;
        return new THREE.CylinderGeometry(radius, radius, height, radialSegments, heightSegments);
      },
      'capsule': function(params) {
        var radius = params.radius,
            length = params.length,
            radialSegments = params.radialSegments || 8,
            heightSegments = params.heightSegments || 1,
            offset = params.offset || false;

        var cylgeo = new THREE.CylinderGeometry(radius, radius, length, radialSegments, heightSegments, true);
        var cap = new THREE.SphereGeometry(radius, radialSegments, radialSegments/2, 0, Math.PI*2, 0, Math.PI/2);
        var cap2 = new THREE.SphereGeometry(radius, radialSegments, radialSegments/2, 0, Math.PI*2, 0, Math.PI/2);
        var mat4 = new THREE.Matrix4();
        mat4.makeTranslation(0, length / 2, 0);
        cap.applyMatrix4(mat4);
        mat4.makeRotationX(Math.PI).setPosition(new THREE.Vector3(0, -length / 2, 0));
        cap2.applyMatrix4(mat4);
        let capsulegeo = THREE.BufferGeometryUtils.mergeBufferGeometries([cylgeo, cap, cap2], false);

        if (offset) {
          capsulegeo.applyMatrix4(mat4.makeTranslation(offset.x, offset.y, offset.z));
        }

        return capsulegeo;
      }
    };
    this.meshes = {};

    /**
     * Call the specified generator, and return either a Geometry or a Mesh, depending on args
     * @param gen {string|array}
     * @param genargs {object}
     * @param meshargs {object}
     * @returns {THREE.Geometry|THREE.Mesh}
     */
    this.generate = function(gen, genargs, meshargs) {
      var geom = false;
      var genname = gen;
      if (elation.utils.isArray(gen)) {
        meshargs = gen[2];
        genargs = gen[1];
        genname = gen[0];
      }
      if (typeof this.generators[genname] == 'function') {
        geom = this.generators[genname](genargs);
      }
      if (geom && meshargs) {
        return this.getmesh(geom, meshargs);
      }
      return geom;
    }
    /**
     * Register a new generator function
     * @param genname {string}
     * @param genfunc {function}
     */
    this.addgenerator = function(genname, genfunc) {
      this.generators[genname] = genfunc;
    }
    /**
     * Returns a mesh with the given parameters set
     * Can take either a generator command (array), a Mesh, or a Geometry
     * @param geometry {array|THREE.Mesh|THREE.Geometry}
     * @param genfunc {function}
     * @returns {THREE.Mesh}
     */
    this.getmesh = function(geometry, meshargs) {
      var mesh = false;
      if (elation.utils.isArray(geometry)) {
        geometry = elation.engine.geometries.generate(geometry);
      }
      if (geometry instanceof THREE.Mesh) {
        mesh = geometry;
      } else if (geometry instanceof THREE.Geometry) {
        mesh = new THREE.Mesh(geometry);
      }
      if (meshargs) {
        for (var k in meshargs) {
          if (mesh[k] && typeof mesh[k].copy == 'function') {
            mesh[k].copy(meshargs[k]);
          } else {
            mesh[k] = meshargs[k];
          }
        }
      }
      return mesh;

    }
    this.setMesh = function(name, mesh) {
      this.meshes[name] = mesh;
    }
    this.loadMeshFromURL = function(name, url, type) {
      if (!this.meshes[name]) {
        // Add a placeholder for newly-added objects so they can be used immediately
        this.meshes[name] = new THREE.Group();
      }
      if (typeof THREE.ColladaLoader == 'undefined') {
        // If the loader hasn't been initialized yet, fetch it!
        THREE.ColladaLoader = false;
        elation.require('engine.external.three.ColladaLoader', elation.bind(this, this.loadMeshFromURL, name, url, type));
      } else if (THREE.ColladaLoader === false) {
        setTimeout(elation.bind(this, this.loadMeshFromURL, name, url, type), 100);
      } else {
        var loader = new THREE.ColladaLoader();
        var xhr = loader.load(url, elation.bind(this, this.handleMeshLoadCollada, name, url));
        elation.events.fire({ type: 'resource_load_start', element: this, data: { type: 'model', url: url } });
      }
      return this.meshes[name];
    }
    this.handleMeshLoadCollada = function(name, url, data) {
      //this.meshes[name] = data.scene;
      // Add the model data as a child of the placeholder we created earlier
      this.meshes[name].add(data.scene);
      //data.scene.rotation.x = -Math.PI/2;
      //data.scene.rotation.z = Math.PI;
/*
      data.scene.traverse(function(n) {
        if (n instanceof THREE.Mesh) {
          var geo = new THREE.BufferGeometry().fromGeometry(n.geometry);
          n.geometry = geo;
        }
      });
*/
      elation.events.fire({ type: 'resource_load_finish', element: this, data: { type: 'model', url: url } });
    }
    this.getMesh = function(name) {
      if (!this.meshes[name]) {
        // If we requested a mesh which hasn't been added yet, create a placeholder so it can be filled in later
        this.meshes[name] = new THREE.Group();
        return this.meshes[name];
      }
      return this.meshes[name].clone();
    }
    this.getMeshGeometry = function(name) {
      if (this.meshes[name] && this.meshes[name] instanceof THREE.Mesh) {
        return this.meshes[name].geometry;
      }
    }
    this.getMeshMaterial = function(name) {
      if (this.meshes[name] && this.meshes[name] instanceof THREE.Mesh) {
        return this.meshes[name].material;
      }
    }
  });

});
<commit_msg>Fix capsule geometry generator<commit_after>elation.require([], function() {
  /**
   * Geometry Generators
   */
  elation.extend("engine.geometries", new function() {
    this.generators = {
      /**
       * Takes an array of geometries and merges them into one
       * @param genargs {array}
       */
      'merge': function(genargs) {
        var geom = new THREE.Geometry();
        for (var i = 0; i < genargs.length; i++) {
          if (genargs[i]) {
            THREE.GeometryUtils.merge(geom, elation.engine.geometries.getmesh(genargs[i]));
          }
        }
        return geom;
      },
      /**
       * Flattens a THREE.Object3D and its object heirarchy into a single geometry
       * @param genargs {object}
       */
      'shrinkwrap': function(genargs) {
        var obj = genargs.obj;
        var geometry = genargs.geometry;
        if (typeof geometry == 'undefined') {
          geometry = new THREE.Geometry();
        }
        if (obj instanceof THREE.Mesh) {
          THREE.GeometryUtils.merge(geometry, obj);
        }
        if (obj.children.length > 0) {
          for (var i = 0; i < obj.children.length; i++) {
            elation.engine.geometries.generate('shrinkwrap', {obj: obj.children[i], geometry: geometry});
          }
        }
        return geometry;
      },
      /**
       * Repeats the specified geometry a number of times with a configurable offset
       * @param genargs {object}
       * @param genargs.geometry {THREE.Geometry|THREE.Mesh}
       * @param genargs.repeat {integer}
       * @param genargs.offset {float}
       * @param genargs.axis {THREE.Vector3}
       * @param genargs.relativeoffset {float}
       */
      'array': function(genargs) {
        var geom = new THREE.Geometry();
        if (genargs.geometry) {
          var base = elation.engine.geometries.getmesh(genargs.geometry);
          var axis = genargs.axis || new THREE.Vector3(1, 0, 0);
          axis.normalize();
          var repeat = genargs.repeat || 1;
          var offset = 0;
          if (genargs.offset) {
            offset = genargs.offset;
          } else if (genargs.relativeoffset) {
            // TODO - relative offset should get the bbox from the base geometry, project it onto the axis, and multiply by the provided scalar
          }
          for (var i = 0; i < repeat; i++) {
            base.position.copy(axis).multiplyScalar(i * offset);
            THREE.GeometryUtils.merge(geom, base);
          }
        }
        return geom;
      },
      'triangle': function(params) {
        var p1 = params.p1 || new THREE.Vector3(),
            p2 = params.p2 || new THREE.Vector3(),
            p3 = params.p3 || new THREE.Vector3();
        var geo = new THREE.BufferGeometry();
        var positions = new Float32Array(3 * 3);

        positions[0] = p1.x;
        positions[1] = p1.y;
        positions[2] = p1.z;

        positions[3] = p2.x;
        positions[4] = p2.y;
        positions[5] = p2.z;

        positions[6] = p3.x;
        positions[7] = p3.y;
        positions[8] = p3.z;

        geo.addAttribute('position', new THREE.BufferAttribute(positions, 3));
        geo.computeFaceNormals();
        geo.computeVertexNormals();

        return geo;
      },
      'sphere': function(params) {
        var radius = params.radius,
            widthSegments = params.heightSegments || 8,
            heightSegments = params.widthSegments || 6,
            phiStart = params.phiStart || 0,
            phiLength = params.phiLength || Math.PI * 2,
            thetaStart = params.thetaStart || 0,
            thetaLength = params.thetaLength || Math.PI;
        var geo = new THREE.SphereBufferGeometry(radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength);
        return geo;
      },
      'box': function(params) {
        var size = params.size || new THREE.Vector3(1,1,1),
            offset = params.offset;

        var geo = new THREE.BoxBufferGeometry(size.x, size.y, size.z, 1, 1, 1);

        if (offset) {
          geo.applyMatrix4(new THREE.Matrix4().makeTranslation(offset.x, offset.y, offset.z));
        }
        return geo;
      },
      'cylinder': function(params) {
        var radius = params.radius,
            height = params.height,
            radialSegments = params.radialSegments || 8,
            heightSegments = params.heightSegments || 1;
        return new THREE.CylinderGeometry(radius, radius, height, radialSegments, heightSegments);
      },
      'capsule': function(params) {
        var radius = params.radius,
            length = params.length,
            radialSegments = params.radialSegments || 8,
            heightSegments = params.heightSegments || 1,
            offset = params.offset || false;

        var cylgeo = new THREE.CylinderGeometry(radius, radius, length, radialSegments, heightSegments, true);
        var cap = new THREE.SphereGeometry(radius, radialSegments, radialSegments/2, 0, Math.PI*2, 0, Math.PI/2);
        var cap2 = new THREE.SphereGeometry(radius, radialSegments, radialSegments/2, 0, Math.PI*2, 0, Math.PI/2);
        var mat4 = new THREE.Matrix4();
        mat4.makeRotationX(Math.PI);
        cap.applyMatrix4(mat4);
        mat4.makeTranslation(0, length / 2, 0);
        cylgeo.applyMatrix4(mat4);
        mat4.setPosition(new THREE.Vector3(0, length, 0));
        cap2.applyMatrix4(mat4);
        let capsulegeo = THREE.BufferGeometryUtils.mergeBufferGeometries([cylgeo, cap, cap2], false);

        if (offset) {
          capsulegeo.applyMatrix4(mat4.makeTranslation(offset.x, offset.y, offset.z));
        }

        return capsulegeo;
      }
    };
    this.meshes = {};

    /**
     * Call the specified generator, and return either a Geometry or a Mesh, depending on args
     * @param gen {string|array}
     * @param genargs {object}
     * @param meshargs {object}
     * @returns {THREE.Geometry|THREE.Mesh}
     */
    this.generate = function(gen, genargs, meshargs) {
      var geom = false;
      var genname = gen;
      if (elation.utils.isArray(gen)) {
        meshargs = gen[2];
        genargs = gen[1];
        genname = gen[0];
      }
      if (typeof this.generators[genname] == 'function') {
        geom = this.generators[genname](genargs);
      }
      if (geom && meshargs) {
        return this.getmesh(geom, meshargs);
      }
      return geom;
    }
    /**
     * Register a new generator function
     * @param genname {string}
     * @param genfunc {function}
     */
    this.addgenerator = function(genname, genfunc) {
      this.generators[genname] = genfunc;
    }
    /**
     * Returns a mesh with the given parameters set
     * Can take either a generator command (array), a Mesh, or a Geometry
     * @param geometry {array|THREE.Mesh|THREE.Geometry}
     * @param genfunc {function}
     * @returns {THREE.Mesh}
     */
    this.getmesh = function(geometry, meshargs) {
      var mesh = false;
      if (elation.utils.isArray(geometry)) {
        geometry = elation.engine.geometries.generate(geometry);
      }
      if (geometry instanceof THREE.Mesh) {
        mesh = geometry;
      } else if (geometry instanceof THREE.Geometry) {
        mesh = new THREE.Mesh(geometry);
      }
      if (meshargs) {
        for (var k in meshargs) {
          if (mesh[k] && typeof mesh[k].copy == 'function') {
            mesh[k].copy(meshargs[k]);
          } else {
            mesh[k] = meshargs[k];
          }
        }
      }
      return mesh;

    }
    this.setMesh = function(name, mesh) {
      this.meshes[name] = mesh;
    }
    this.loadMeshFromURL = function(name, url, type) {
      if (!this.meshes[name]) {
        // Add a placeholder for newly-added objects so they can be used immediately
        this.meshes[name] = new THREE.Group();
      }
      if (typeof THREE.ColladaLoader == 'undefined') {
        // If the loader hasn't been initialized yet, fetch it!
        THREE.ColladaLoader = false;
        elation.require('engine.external.three.ColladaLoader', elation.bind(this, this.loadMeshFromURL, name, url, type));
      } else if (THREE.ColladaLoader === false) {
        setTimeout(elation.bind(this, this.loadMeshFromURL, name, url, type), 100);
      } else {
        var loader = new THREE.ColladaLoader();
        var xhr = loader.load(url, elation.bind(this, this.handleMeshLoadCollada, name, url));
        elation.events.fire({ type: 'resource_load_start', element: this, data: { type: 'model', url: url } });
      }
      return this.meshes[name];
    }
    this.handleMeshLoadCollada = function(name, url, data) {
      //this.meshes[name] = data.scene;
      // Add the model data as a child of the placeholder we created earlier
      this.meshes[name].add(data.scene);
      //data.scene.rotation.x = -Math.PI/2;
      //data.scene.rotation.z = Math.PI;
/*
      data.scene.traverse(function(n) {
        if (n instanceof THREE.Mesh) {
          var geo = new THREE.BufferGeometry().fromGeometry(n.geometry);
          n.geometry = geo;
        }
      });
*/
      elation.events.fire({ type: 'resource_load_finish', element: this, data: { type: 'model', url: url } });
    }
    this.getMesh = function(name) {
      if (!this.meshes[name]) {
        // If we requested a mesh which hasn't been added yet, create a placeholder so it can be filled in later
        this.meshes[name] = new THREE.Group();
        return this.meshes[name];
      }
      return this.meshes[name].clone();
    }
    this.getMeshGeometry = function(name) {
      if (this.meshes[name] && this.meshes[name] instanceof THREE.Mesh) {
        return this.meshes[name].geometry;
      }
    }
    this.getMeshMaterial = function(name) {
      if (this.meshes[name] && this.meshes[name] instanceof THREE.Mesh) {
        return this.meshes[name].material;
      }
    }
  });

});
<|endoftext|>


<commit_before>(function(b){if(!b.a){var f=function(){};f.prototype.get=function(a){return this[a]};f.prototype.set=function(a,c){this[a]=c};b.a=function(a,c){if(!(a instanceof Object&&c instanceof Object))throw new TypeError("Cannot create proxy with a non-object as target or handler");var b={get:!0,set:!0,apply:!0},g;for(g in c)if(!b[g])throw new TypeError("Proxy polyfill does not support trap '"+g+"'");var d=this,h=!1;"apply"in c?(d=function(){return c.apply(a,this,arguments)},h=!0):a instanceof Function&&(d=
function(){return a.apply(this,arguments)},h=!0);var e=new f;c.get&&(e.get=function(a){return c.get(this,a,d)});c.set&&(e.set=function(a,b){c.set(this,a,b,d)});Object.getOwnPropertyNames(a).forEach(function(b){var c={enumerable:!!Object.getOwnPropertyDescriptor(a,b).enumerable};c.get=e.get.bind(a,b);c.set=e.set.bind(a,b);h&&b in d||Object.defineProperty(d,b,c)});Object.seal(a);Object.seal(d);return d};b.a.b=function(a,c){return{c:new b.a(a,c),f:function(){throw Error("revoke not implemented");}}};
b.a.revocable=b.a.b;b.Proxy=b.a}})(window);
<commit_msg>update min<commit_after>(function(c){if(!c.a){var h=function(){};h.prototype.get=function(b){return this[b]};h.prototype.set=function(b,a){this[b]=a};c.a=function(b,a){if(!(b instanceof Object&&a instanceof Object))throw new TypeError("Cannot create proxy with a non-object as target or handler");var c=a;a={};var l={get:!0,set:!0,apply:!0,construct:!0},e;for(e in c){if(!l[e])throw new TypeError("Proxy polyfill does not support trap '"+e+"'");a[e]=c[e]}var d=this,k=!1;if("apply"in a||"construct"in a||b instanceof Function)d=
function(){var f=this&&this.constructor===d;if(f&&"construct"in a)return a.c.call(this,b,arguments);if(!f&&"apply"in a)return a.apply(b,this,arguments);if(b instanceof Function)return f?new b(arguments):b.apply(this,arguments);throw new TypeError(f?"not a constructor":"not a function");},k=!0;var g=new h;a.get&&(g.get=function(b){return a.get(this,b,d)});a.set&&(g.set=function(b,c){a.set(this,b,c,d)});Object.getOwnPropertyNames(b).forEach(function(a){var c={enumerable:!!Object.getOwnPropertyDescriptor(b,
a).enumerable};c.get=g.get.bind(b,a);c.set=g.set.bind(b,a);k&&a in d||Object.defineProperty(d,a,c)});Object.seal(b);Object.seal(d);return d};c.a.b=function(b,a){return{f:new c.a(b,a),g:function(){throw Error("revoke not implemented");}}};c.a.revocable=c.a.b;c.Proxy=c.a}})(window);
<|endoftext|>


<commit_before>var events = require('../apis/github/users/eklemen/events.json'),
    assert = require('assert'),
    _ = require('lodash');

console.log(Date());

    it('should tell us that events has an input', function (){
        assert(events);
    });

function answer(){
    var pushEvents = events.filter(function(item){
        return item.type == 'PushEvent';
    });

    var uniqueTypes = _.filter(events, function() {_.uniq(events.type);
            });

    return {
        'total': events.length,
        'PushEvent': {
            'total': pushEvents.length,
        },
        'unique': 0
    };
}
console.log(answer());
console.log(events.length);
var theAnswer = answer();

// store in new array all event types

    it('should return that answer exists, and test for total events', function(){
        assert(answer);
        assert(events.length === 30);
    });

    it('should return the length when calling answer function', function(){
       assert.equal(theAnswer.total, 30);
    });

    it('should have "PushEvent", and has a total count of PushEvents', function(){
        assert(theAnswer.PushEvent);
        assert(theAnswer.PushEvent.total);
    });

    it ('should find all unique `type`s', fucntion(){
        assert(theAnswer.unique);
    });

// THE CODE BELOW IS FROM MONDAY

//console.log(events.length); //prints 30, I'm assuming 30 events but that doesnt seem right hmm...
//
//assert(events.length);
//
//function answer(){
//    var index = 0;
//    var numberOfPushEvents = 0;
//    // can leave off index and array if not using them
//
//    events.forEach(function(item, index, array){
//        if(item.type == "PushEvent"){
//            numberOfPushEvents++;
//        }
//    });
//
//    var pushEvents = events.filter(function(item){
//        return item.type == "PushEvent";
//
//    });
//    pushEvents.length == numberOfPushEvents;
//
//    return {
//        'total': events.length,
//        'PushEvent': {
//            'total': numberOfPushEvents,
//        }
//    };
//}
//
//describe('Should determine that events and answer exist, also what type they are', function(){
//it('should be test for events and answer', function(){
//    assert(events);
//    assert(events instanceof Array);
//    assert(answer);
//    assert(typeof answer == 'function');
//})
//})
//
//describe("tests for key values", function(){
//    it('should have 30 total events', function(){
//        assert(events.length > 0)
//        assert.equal(events.length, 30)
//    })
//
//})

//    while (index < events.length) {
//        var event = events[index];
//
//        if (event.type == "PushEvent"){
//            numberOfPushEvents++;
//        }
//
//        index++;
//    }
//
//
//    return {
//        'total': events.length
//    };
// end answer
//
//describe('the setup', function (){
//
//    it('should have events', function (){
//        assert(events);
//    });
//
//    it('should have an "answer" function', function(){
//        assert(answer);
//        assert(typeof answer == 'function');
//    });
//});
//describe('the answer', function (){
//    var theAnswer = answer ();
//
//    it('should have 30 total events', function (){
//        assert(theAnswer.total === 30);
//    });
//
//    it('should have some `PushEvent` entries', function(){
//        assert(theAnswer.PushEvents);
//        assert(theAnswer.PushEvents.total);
//    });
//});<commit_msg>Added gymnastics<commit_after>var events = require('../apis/github/users/eklemen/events.json'),
    assert = require('assert'),
    _ = require('lodash');

console.log(Date());

    it('should tell us that events has an input', function (){
        assert(events);
    });

function answer(){
    var pushEvents = _.filter(events, {'type': 'PushEvent'}
                            );

//    var uniqueTypes = _.filter(events, {'type': 'PushEvent'}
//                              );
//

    return {
        'total': events.length,
        'PushEvent': {
            'total': pushEvents.length,
        }
    };
}
var theAnswer = answer();
var first = _.first(events);
var last = _.last(events);

console.log(
    _(events)
        .pluck(events, 'created_at')
        .map(fucntion(date) {
             return date.slice(0, 10);
        })
        .uniq()
    .value()
);

it('should have `perDay` stats', function(){
    assert(first);
    assert(first.created_at);
    console.log(first.created_at.slice(8, 10));
    console.log(last.created_at.slice(8, 10));
});

// store in new array all event types

    it('should return that answer exists, and test for total events', function(){
        assert(answer);
        assert(events.length === 30);
    });

    it('should return the length when calling answer function', function(){
       assert.equal(theAnswer.total, 30);
    });

    it('should have "PushEvent", and has a total count of PushEvents', function(){
        assert(theAnswer.PushEvent);
        assert(theAnswer.PushEvent.total);
    });

//    it ('should find all unique `type`s', fucntion(){
//        assert(theAnswer.unique);
//    });

// THE CODE BELOW IS FROM MONDAY

//console.log(events.length); //prints 30, I'm assuming 30 events but that doesnt seem right hmm...
//
//assert(events.length);
//
//function answer(){
//    var index = 0;
//    var numberOfPushEvents = 0;
//    // can leave off index and array if not using them
//
//    events.forEach(function(item, index, array){
//        if(item.type == "PushEvent"){
//            numberOfPushEvents++;
//        }
//    });
//
//    var pushEvents = events.filter(function(item){
//        return item.type == "PushEvent";
//
//    });
//    pushEvents.length == numberOfPushEvents;
//
//    return {
//        'total': events.length,
//        'PushEvent': {
//            'total': numberOfPushEvents,
//        }
//    };
//}
//
//describe('Should determine that events and answer exist, also what type they are', function(){
//it('should be test for events and answer', function(){
//    assert(events);
//    assert(events instanceof Array);
//    assert(answer);
//    assert(typeof answer == 'function');
//})
//})
//
//describe("tests for key values", function(){
//    it('should have 30 total events', function(){
//        assert(events.length > 0)
//        assert.equal(events.length, 30)
//    })
//
//})

//    while (index < events.length) {
//        var event = events[index];
//
//        if (event.type == "PushEvent"){
//            numberOfPushEvents++;
//        }
//
//        index++;
//    }
//
//
//    return {
//        'total': events.length
//    };
// end answer
//
//describe('the setup', function (){
//
//    it('should have events', function (){
//        assert(events);
//    });
//
//    it('should have an "answer" function', function(){
//        assert(answer);
//        assert(typeof answer == 'function');
//    });
//});
//describe('the answer', function (){
//    var theAnswer = answer ();
//
//    it('should have 30 total events', function (){
//        assert(theAnswer.total === 30);
//    });
//
//    it('should have some `PushEvent` entries', function(){
//        assert(theAnswer.PushEvents);
//        assert(theAnswer.PushEvents.total);
//    });
//});<|endoftext|>


<commit_before>'use strict';

const version = 'v1';
const offlineResources = [
  '/',
  '/offline.html'
];

self.addEventListener('install', (event) => {
  log('install event in progress.');

  event.waitUntil(
    caches
    .open(cachekey('offline'))
    .then((cache) => {
      return cache.addAll(offlineResources);
    })
    .then(() => {
      self.skipWaiting();
      log('installation complete!');
    })
  );
});

self.addEventListener('fetch', (event) => {
  log('fetch event', event);

  /* We should only cache GET requests, and deal with the rest of method in the
     client-side, by handling failed POST,PUT,PATCH,etc. requests.
     */
  if (ignoreFetchEvent(event)) {
    /* If we don't block the event as shown below, then the request will go to
       the network as usual.
       */
    log('fetch event ignored.', event.request.method, event.request.url);
    return;
  }

  /* Similar to event.waitUntil in that it blocks the fetch event on a promise.
     Fulfillment result will be used as the response, and rejection will end in a
     HTTP response indicating failure.
     */
  event.respondWith(
    caches
    .match(event.request)
    .then((cached) => {
      /* Even if the response is in our cache, we go to the network as well.
         This pattern is known for producing "eventually fresh" responses,
         where we return cached responses immediately, and meanwhile pull
         a network response and store that in the cache.
         Read more:
https://ponyfoo.com/articles/progressive-networking-serviceworker
*/
      let networked = fetch(event.request).then(fetchedFromNetwork(event));
      // .then(fetchedFromNetwork(event), unableToResolve(event))
      // .catch(unableToResolve);

      /* We return the cached response immediately if there is one, and fall
         back to waiting on the network as usual.
         */
      log('fetch event', cached ? '(cached)' : '(network)', event.request.url);

      return cached || networked;
    })
  );
});

self.addEventListener("activate", (event) => {
  /* Just like with the install event, event.waitUntil blocks activate on a promise.
     Activation will fail unless the promise is fulfilled.
     */
  log('activate event in progress.');

  event.waitUntil(
    caches
    // This method returns a promise which will resolve to an array of available cache keys.
    .keys()
    .then((keys) => {
      // We return a promise that settles when all outdated caches are deleted.
      return Promise.all(
        keys
        .filter((key) => {
          return !key.startsWith(version); // Filter by keys that don't start with the latest version prefix.
        })
        .map((key) => {
          return caches.delete(key); // Return a promise that's fulfilled when each outdated cache is deleted.
        })
      );
    })
    .then(() => {
      log('activate completed.');
    })
  );
});

function cachekey() {
  return [version, ...arguments].join(':');
}

function log() {
  if (__DEVELOPMENT__) {
    console.log("SW:", ...arguments);
  }
}

function fetchedFromNetwork(event) {
  return function cacheAndRespond(response) {
    /* We copy the response before replying to the network request.
       This is the response that will be stored on the ServiceWorker cache.
       */
    let cacheCopy = response.clone();
    log('fetch response from network', event.request.url);

    // We open a cache to store the response for this request
    caches
    .open(cachekey('pages'))
    .then((cache) => {
      /* We store the response for this request. It'll later become
         available to caches.match(event.request) calls, when looking
         for cached responses.
         */
      cache.put(event.request, cacheCopy);
    })
    .then(() => {
      log('fetch response stored in cache.', event.request.url);
    });

    return response;
  }
}

function unableToResolve(event) {
  return function handleRequestFailure(response) {
    /* When this method is called, it means we were unable to produce a response
       from either the cache or the network. This is our opportunity to produce
       a meaningful response even when all else fails. It's the last chance, so
       you probably want to display a "Service Unavailable" view or a generic
       error response.
       There's a couple of things we can do here.
       - Test the Accept header and then return one of the `offlineFundamentals`
       e.g: `return caches.match('/some/cached/image.png')`
       - You should also consider the origin. It's easier to decide what
       "unavailable" means for requests against your origins than for requests
       against a third party, such as an ad provider
       - Generate a Response programmaticaly, as shown below, and return that
          return new Response('
Service Unavailable
', {
            status: 503,
            statusText: 'Service Unavailable',
            headers: new Headers({
              'Content-Type': 'text/html'
            })
          });
       */
    log('fetch request failed in both cache and network', event);
  }
}

function ignoreFetchEvent(event) {
  return event.request.method !== 'GET' ||
     event.request.url.match(/\/__rack\//);
}

log("Hello from ServiceWorker land!", version);
<commit_msg>Ignore fetch for api.mixpanel.com events<commit_after>'use strict';

const version = 'v1';
const offlineResources = [
  '/',
  '/offline.html'
];

var ignoreFetch = [
  /https?:\/\/api.mixpanel.com\//,
  /https?:\/\/api.segment.io\//,
  /https?:\/\/in.getclicky.com\//,
  /\/__rack\//,
]

self.addEventListener('install', (event) => {
  log('install event in progress.');

  event.waitUntil(
    caches
    .open(cachekey('offline'))
    .then((cache) => {
      return cache.addAll(offlineResources);
    })
    .then(() => {
      self.skipWaiting();
      log('installation complete!');
    })
  );
});

self.addEventListener('fetch', (event) => {
  log('fetch event', event);

  /* We should only cache GET requests, and deal with the rest of method in the
     client-side, by handling failed POST,PUT,PATCH,etc. requests.
     */
  if (ignoreFetchEvent(event)) {
    /* If we don't block the event as shown below, then the request will go to
       the network as usual.
       */
    log('fetch event ignored.', event.request.method, event.request.url);
    return;
  }

  /* Similar to event.waitUntil in that it blocks the fetch event on a promise.
     Fulfillment result will be used as the response, and rejection will end in a
     HTTP response indicating failure.
     */
  event.respondWith(
    caches
    .match(event.request)
    .then((cached) => {
      /* Even if the response is in our cache, we go to the network as well.
         This pattern is known for producing "eventually fresh" responses,
         where we return cached responses immediately, and meanwhile pull
         a network response and store that in the cache.
         Read more:
https://ponyfoo.com/articles/progressive-networking-serviceworker
*/
      let networked = fetch(event.request).then(fetchedFromNetwork(event));
      // .then(fetchedFromNetwork(event), unableToResolve(event))
      // .catch(unableToResolve);

      /* We return the cached response immediately if there is one, and fall
         back to waiting on the network as usual.
         */
      log('fetch event', cached ? '(cached)' : '(network)', event.request.url);

      return cached || networked;
    })
  );
});

self.addEventListener("activate", (event) => {
  /* Just like with the install event, event.waitUntil blocks activate on a promise.
     Activation will fail unless the promise is fulfilled.
     */
  log('activate event in progress.');

  event.waitUntil(
    caches
    // This method returns a promise which will resolve to an array of available cache keys.
    .keys()
    .then((keys) => {
      // We return a promise that settles when all outdated caches are deleted.
      return Promise.all(
        keys
        .filter((key) => {
          return !key.startsWith(version); // Filter by keys that don't start with the latest version prefix.
        })
        .map((key) => {
          return caches.delete(key); // Return a promise that's fulfilled when each outdated cache is deleted.
        })
      );
    })
    .then(() => {
      log('activate completed.');
    })
  );
});

function cachekey() {
  return [version, ...arguments].join(':');
}

function log() {
  if (__DEVELOPMENT__) {
    console.log("SW:", ...arguments);
  }
}

function fetchedFromNetwork(event) {
  return function cacheAndRespond(response) {
    /* We copy the response before replying to the network request.
       This is the response that will be stored on the ServiceWorker cache.
       */
    let cacheCopy = response.clone();
    log('fetch response from network', event.request.url);

    // We open a cache to store the response for this request
    caches
    .open(cachekey('pages'))
    .then((cache) => {
      /* We store the response for this request. It'll later become
         available to caches.match(event.request) calls, when looking
         for cached responses.
         */
      cache.put(event.request, cacheCopy);
    })
    .then(() => {
      log('fetch response stored in cache.', event.request.url);
    });

    return response;
  }
}

function unableToResolve(event) {
  return function handleRequestFailure(response) {
    /* When this method is called, it means we were unable to produce a response
       from either the cache or the network. This is our opportunity to produce
       a meaningful response even when all else fails. It's the last chance, so
       you probably want to display a "Service Unavailable" view or a generic
       error response.
       There's a couple of things we can do here.
       - Test the Accept header and then return one of the `offlineFundamentals`
       e.g: `return caches.match('/some/cached/image.png')`
       - You should also consider the origin. It's easier to decide what
       "unavailable" means for requests against your origins than for requests
       against a third party, such as an ad provider
       - Generate a Response programmaticaly, as shown below, and return that
          return new Response('
Service Unavailable
', {
            status: 503,
            statusText: 'Service Unavailable',
            headers: new Headers({
              'Content-Type': 'text/html'
            })
          });
       */
    log('fetch request failed in both cache and network', event);
  }
}

function ignoreFetchEvent(event) {
  return event.request.method !== 'GET' || ignoreFetch.some(regex => event.request.url.match(regex));
}

log("Hello from ServiceWorker land!", version);
<|endoftext|>


<commit_before>this.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open('learn-memory').then(function(cache) {
      return cache.addAll([
        '/',
        '/stylesheets/styles.css',
        '/views/lessons-list.html',
        '/views/lessons-id.html',
        '/javascripts/scripts.js',
        '/langs/locale-en.json'
      ]);
    })
  );
});

this.addEventListener('fetch', function(event) {
  if (event.request.method !== 'GET' || /authenticated/.test(event.request.url)) {
    return;
  }
  event.respondWith(
    caches
      .match(event.request)
      .then(function(cached) {
        // Get the latest updates from API
        if (/api/.test(event.request.url)) {
          return fetch(event.request).then(function(response) {
            return caches.open('learn-memory').then(function(cache) {
              cache.put(event.request, response.clone());
              return response;
            });
          }).catch(function () {
            return cached;
          });
        }

        if (typeof cached == 'undefined') {
      	  return fetch(event.request).then(function(response) {
      		  return caches.open('learn-memory-2').then(function(cache) {
      		    cache.put(event.request, response.clone());
      		    return response;
      		  });
      		});
      	}

        return cached;

      })
      .catch(function() {
        fetch(event.request).then(function(response) {
          return caches.open('learn-memory').then(function(cache) {
            cache.put(event.request, response.clone());
            return response;
          });
        });
     })
  );
});
<commit_msg>Reorder service worker code<commit_after>this.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open('learn-memory').then(function(cache) {
      return cache.addAll([
        '/',
        '/stylesheets/styles.css',
        '/views/lessons-list.html',
        '/views/lessons-id.html',
        '/javascripts/scripts.js',
        '/langs/locale-en.json'
      ]);
    })
  );
});

this.addEventListener('fetch', function(event) {
  if (event.request.method !== 'GET' || /authenticated/.test(event.request.url)) {
    return;
  }
  var get = function () {
    return fetch(event.request).then(function(response) {
      return caches.open('learn-memory').then(function(cache) {
        cache.put(event.request, response.clone());
        return response;
      });
    });
  };

  event.respondWith(
    caches
      .match(event.request)
      .then(function(cached) {
        // get the latest updates from API
        if (/api/.test(event.request.url)) {
          return get().catch(function () {
            return cached;
          });
        }

        // the cached value could be undefined
        if (typeof cached == 'undefined') {
      	  return get();
      	}

        return cached;
      })
      .catch(get)
  );
});
<|endoftext|>


<commit_before>// creates array of matches
// _.flatten(d3.values(data).map(function(d){ return d3.values(d); }))

var height = 500,
		width = 750,
		margin = {left: 20, right: 20, top: 20, bottom: 20};

var x = d3.scale.linear()
		.domain([0, 18])
		.range([0, width])

var y = d3.scale.linear()
		.domain([-9, 9])
		.range([height, 0])

var xTick = x(1),
		yTick = y(8);

var radiusScale = d3.scale.sqrt()
		.range([0, 10])

var lineWidthScale = d3.scale.linear()
		.range([0, 1, 8])

var color = d3.scale.category10();

var svg = d3.select('#golf-wl')
  .append('svg')
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")")

var holeConstrains = {"4":[1],"8":[-1, 3]},
		rounds = [],
		directions = ['down', 'same', 'up']

d3.range(0, 19).forEach(function(hole){
	d3.range(-9, 10).forEach(function(spread){
		if (9 - Math.abs(9 - hole) >= Math.abs(spread)){
			rounds.push({hole: hole, spread: spread})
		}
	})
})

var roundGs = svg.selectAll('.roundG')
		.data(rounds).enter()
	.append('g')
		.attr('transform', function(d){
			return ['translate(', x(d.hole), ',', y(d.spread), ')'].join(''); })

d3.json('flat-data.json', function(err, matches){
	function updateData(){
		rounds.forEach(function(d){
			directions.concat('count').forEach(function(str){ d[str] = 0 }) })

		var holeConstrainsArray = d3.entries(holeConstrains);

		matches.forEach(function(match){
			//winner of the first match always on top
			var flip = false;
			match.scores.some(function(d){
				if (d < 0){ flip = true}
				return d != 0;
			})
			if (flip){
				match.scores = match.scores.map(function(d){ return -1*d; })
			}

			//don't count matches that don't meet constraints
			var meetsConstraints = holeConstrainsArray.every(function(d){
				//debugger;
				return _.contains(d.value, match.scores[d.key])
			})
			if (!meetsConstraints) return

			match.scores.forEach(function(spread, hole){
				var round = _.findWhere(rounds, {hole: hole, spread: spread});
				if (!round) return;
				round.count++;
				var nextSpread = match.scores[hole+1];
				if (isNaN(nextSpread)) return
				round[nextSpread < spread ? 'down' : nextSpread == spread ? 'same' : 'up']++;
			})
		})
	}
	updateData();

	function updateScales(){
		radiusScale.domain(d3.extent(rounds, f('count')))
		var maxLineVals = directions.map(function(str){ return d3.max(rounds, ƒ(str)) })
		lineWidthScale.domain([0, 1, d3.max(maxLineVals)])
	}
	updateScales();

	roundGs.selectAll('line')
			.data(function(d){
				return directions.map(function(str, i){
					return {type: str, count: d[str], direction: i - 1}
				}) })
			.enter()
		.append('line')
			.attr({x2: xTick})
			.attr('y2', function(d){ return d.direction*(-yTick)})
			.style('stroke-width', _.compose(lineWidthScale, f('count')))
			.style('stroke', _.compose(color, f('type')))

	roundGs.append('circle')
			.attr('r', _.compose(radiusScale, f('count')))

	roundGs.append('rect')
			.attr({x: -xTick/2, y: -yTick/2, width: xTick, height: yTick})
			.on('mouseover', function(d){
				d3.select(this).classed('hovered', true)
			})
			.on('mouseout', function(d){
				d3.select(this).classed('hovered', false)
			})
			.on('click', function(d){
				var selected = !d3.select(this).classed('selected')
				d3.select(this).classed('selected', selected)
				if (selected){
					if (holeConstrains[d.hole]){
						holeConstrains[d.hole].push(d.spread);
					} else{
						holeConstrains[d.hole] = [d.spread];
					}
				} else{
					holeConstrains[d.hole] = holeConstrains[d.hole]
						.filter(function(spread){ return spread != d.spread; })

					if (!holeConstrains[d.hole].length){
						delete holeConstrains[d.hole];
					}
				}
			})
})<commit_msg>magic working!<commit_after>// creates array of matches
// _.flatten(d3.values(data).map(function(d){ return d3.values(d); }))

var height = 500,
		width = 750,
		margin = {left: 20, right: 20, top: 20, bottom: 20};

var x = d3.scale.linear()
		.domain([0, 18])
		.range([0, width])

var y = d3.scale.linear()
		.domain([-9, 9])
		.range([height, 0])

var xTick = x(1),
		yTick = y(8);

var radiusScale = d3.scale.sqrt()
		.range([0, 10])

var lineWidthScale = d3.scale.linear()
		.range([0, 1, 8])

var color = d3.scale.category10();

var svg = d3.select('#golf-wl')
  .append('svg')
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")")

var holeConstrains = {"4":[1],"8":[-1, 3]},
		rounds = [],
		directions = ['down', 'same', 'up']

d3.range(0, 19).forEach(function(hole){
	d3.range(-9, 10).forEach(function(spread){
		if (9 - Math.abs(9 - hole) >= Math.abs(spread)){
			rounds.push({hole: hole, spread: spread})
		}
	})
})

var roundGs = svg.selectAll('.roundG')
		.data(rounds).enter()
	.append('g')
		.attr('transform', function(d){
			return ['translate(', x(d.hole), ',', y(d.spread), ')'].join(''); })

d3.json('flat-data.json', function(err, matches){
	function updateData(){
		rounds.forEach(function(d){
			directions.concat('count').forEach(function(str){ d[str] = 0 }) })

		var holeConstrainsArray = d3.entries(holeConstrains);

		matches.forEach(function(match){
			//winner of the first match always on top
			var flip = false;
			match.scores.some(function(d){
				if (d < 0){ flip = true}
				return d != 0;
			})
			if (flip){
				match.scores = match.scores.map(function(d){ return -1*d; })
			}

			//don't count matches that don't meet constraints
			var meetsConstraints = holeConstrainsArray.every(function(d){
				//debugger;
				return _.contains(d.value, match.scores[d.key])
			})
			if (!meetsConstraints) return

			match.scores.forEach(function(spread, hole){
				var round = _.findWhere(rounds, {hole: hole, spread: spread});
				if (!round) return;
				round.count++;
				var nextSpread = match.scores[hole+1];
				if (isNaN(nextSpread)) return
				round[nextSpread < spread ? 'down' : nextSpread == spread ? 'same' : 'up']++;
			})
		})
	}
	updateData();

	function updateScales(){
		radiusScale.domain(d3.extent(rounds, f('count')))
		var maxLineVals = directions.map(function(str){ return d3.max(rounds, ƒ(str)) })
		lineWidthScale.domain([0, 1, d3.max(maxLineVals)])
	}
	updateScales();

	function firstDraw(){
		roundGs.selectAll('line')
				.data(function(d){
					return directions.map(function(str, i){
						return {type: str, direction: i - 1}
					}) })
				.enter()
			.append('line')
				.attr({x2: xTick})
				.attr('y2', function(d){ return d.direction*(-yTick)})

		roundGs.append('circle')

		roundGs.append('rect')
				.attr({x: -xTick/2, y: -yTick/2, width: xTick, height: yTick})
				.on('mouseover', function(d){
					d3.select(this).classed('hovered', true)
				})
				.on('mouseout', function(d){
					d3.select(this).classed('hovered', false)
				})
				.on('click', function(d){
					var selected = !d3.select(this).classed('selected')
					d3.select(this).classed('selected', selected)
					if (selected){
						if (holeConstrains[d.hole]){
							holeConstrains[d.hole].push(d.spread);
						} else{
							holeConstrains[d.hole] = [d.spread];
						}
					} else{
						holeConstrains[d.hole] = holeConstrains[d.hole]
							.filter(function(spread){ return spread != d.spread; })

						if (!holeConstrains[d.hole].length){
							delete holeConstrains[d.hole];
						}
					}

					updateData();
					updateScales();
					updateDOM();
				})
	}
	firstDraw();

	function updateDOM(){
		roundGs.selectAll('line')
				.data(function(d){
					return directions.map(function(str, i){
						return {type: str, count: d[str], direction: i - 1}
					}) })
				.style('stroke-width', _.compose(lineWidthScale, f('count')))
				.style('stroke', _.compose(color, f('type')))

		roundGs.select('circle')
				.attr('r', _.compose(radiusScale, f('count')))
	}
	updateDOM();
})<|endoftext|>


<commit_before>/* globals $ */
var popover = function() {
  var options = {
    html: true,
    placement: function(context, source) {
      var position = $(source).offset();
      if ($(source).hasClass('a-js-popoverBig')) {
        return 'bottom';
      }
      if (position.left < 125) {
        return 'right';
      }
      if (position.left > ($(document).width() - $(source).width() - 125)) {
        return 'left';
      }
      return 'bottom';
    },
    content: function() {
      if ($(this).attr('data-popover-content')) {
        return $('#' + $(this).data('popover-content')).html();
      }
      return false;
    },
    trigger: 'click'
  };
  function adjustBig() {
    if ($('.popover-big').length > 0) {
      $('.popover-big').attr('style',
        $('.popover-big').attr('style').replace(
          /translateX\(.*?\)/, 'translateX(0px)'
        )
      );
    }
  }
  $('[data-toggle="popover"]').each(function() {
    if ($(this).attr('data-template')) {
      $(this).attr('data-template', $(this).attr('data-template').replace(/<!--[\s\S]*?-->/g, ''));
    }
  });
  $('[data-toggle="popover"]').popover(options);
  $('#example').popover();
  $('.a-js-togglePopoverIcons').each(function() {
    $(this).find('i').eq(1).hide();
  });
  $('.a-js-persistPopover').find('i').eq(0).on('click', function(e) {
    $(e.target).closest('.a-js-persistPopover').popover('show');
  });
  $('.a-js-persistPopover').find('i').eq(1).on('click', function(e) {
    $(e.target).closest('.a-js-persistPopover').popover('hide');
  });
  $('.a-js-blurrablePopover').on('blur', function(e) {
    if ($(e.target).find('i').eq(1).is(':visible')) {
      $(e.target).trigger('click');
    }
  });
  $('body').on('mousedown', '.bs-tether-element *', function(e) {
    e.preventDefault(); e.stopPropagation();
  });
  $('body').on('mousedown', '.bs-tether-element', function(e) {
    e.preventDefault(); e.stopPropagation();
  });
  $('.a-js-togglePopoverIcons').on('shown.bs.popover', function(e) {
    $(e.target).find('i').eq(0).hide(); $(e.target).find('i').eq(1).show();
  });
  $('.a-js-togglePopoverIcons').on('hidden.bs.popover', function(e) {
    $(e.target).find('i').eq(0).show(); $(e.target).find('i').eq(1).hide();
  });
  $('.a-js-persistPopover').on('shown.bs.popover', function() {
    adjustBig();
    // $('.popover-big').find('.popover-arrow').css(
    //   'left', ($(this).offset().left + 9) + 'px'
    // );
    // $('.popover-big').attr('data-arrowleftadjust', ($(this).offset().left + 9) + 'px');
    $('body').append(
      '<style>.popover-big:after { left: ' + ($(this).offset().left + 10.5) + 'px !important; }</style>');
    $('html, body').animate({
      scrollTop: $('.a-js-persistPopover').offset().top - 50
    }, 250);
  });
  $(window).scroll(adjustBig); $(window).resize(adjustBig);
};
<commit_msg>Misc.<commit_after>/* globals $ */
var popover = function() {
  var options = {
    html: true,
    placement: function(context, source) {
      var position = $(source).offset();
      if ($(source).hasClass('a-js-popoverBig')) {
        return 'bottom';
      }
      if (position.left < 125) {
        return 'right';
      }
      if (position.left > ($(document).width() - $(source).width() - 125)) {
        return 'left';
      }
      return 'bottom';
    },
    content: function() {
      if ($(this).attr('data-popover-content')) {
        return $('#' + $(this).data('popover-content')).html();
      }
      return false;
    },
    trigger: 'click'
  };
  function adjustBig() {
    if ($('.popover-big').length > 0) {
      $('.popover-big').attr('style',
        $('.popover-big').attr('style').replace(
          /translateX\(.*?\)/, 'translateX(0px)'
        )
      );
    }
  }
  $('[data-toggle="popover"]').each(function() {
    if ($(this).attr('data-template')) {
      $(this).attr('data-template', $(this).attr('data-template').replace(/<!--[\s\S]*?-->/g, ''));
    }
  });
  $('[data-toggle="popover"]').popover(options);
  $('#example').popover();
  $('.a-js-togglePopoverIcons').each(function() {
    $(this).find('i').eq(1).hide();
  });
  $('.a-js-persistPopover').find('i').eq(0).on('click', function(e) {
    $(e.target).closest('.a-js-persistPopover').popover('show');
  });
  $('.a-js-persistPopover').find('i').eq(1).on('click', function(e) {
    $(e.target).closest('.a-js-persistPopover').popover('hide');
  });
  $('.a-js-blurrablePopover').on('blur', function(e) {
    if ($(e.target).find('i').eq(1).is(':visible')) {
      $(e.target).trigger('click');
    }
  });
  $('body').on('mouseup', '.bs-tether-element *', function(e) {
    e.preventDefault(); e.stopPropagation();
  });
  $('body').on('mouseup', '.bs-tether-element', function(e) {
    e.preventDefault(); e.stopPropagation();
  });
  $('.a-js-togglePopoverIcons').on('shown.bs.popover', function(e) {
    $(e.target).find('i').eq(0).hide(); $(e.target).find('i').eq(1).show();
  });
  $('.a-js-togglePopoverIcons').on('hidden.bs.popover', function(e) {
    $(e.target).find('i').eq(0).show(); $(e.target).find('i').eq(1).hide();
  });
  $('.a-js-persistPopover').on('shown.bs.popover', function() {
    adjustBig();
    // $('.popover-big').find('.popover-arrow').css(
    //   'left', ($(this).offset().left + 9) + 'px'
    // );
    // $('.popover-big').attr('data-arrowleftadjust', ($(this).offset().left + 9) + 'px');
    $('body').append(
      '<style>.popover-big:after { left: ' + ($(this).offset().left + 10.5) + 'px !important; }</style>');
    $('html, body').animate({
      scrollTop: $('.a-js-persistPopover').offset().top - 50
    }, 250);
  });
  $(window).scroll(adjustBig); $(window).resize(adjustBig);
};
<|endoftext|>