block by vlandham 2549b64121112dd9e4dacc47c959472a

2549b64121112dd9e4da

Full Screen

index.html

<!doctype html>
<!--[if lt IE 7]> <html class="no-js ie6 oldie" lang="en"> <![endif]-->
<!--[if IE 7]>    <html class="no-js ie7 oldie" lang="en"> <![endif]-->
<!--[if IE 8]>    <html class="no-js ie8 oldie" lang="en"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en"> <!--<![endif]-->
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">

  <title>vis</title>
  <meta name="description" content="">
  <meta name="author" content="">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">


  <!-- CSS concatenated and minified via ant build script-->
  <link rel="stylesheet" href="bootstrap.min.css">
  <link rel="stylesheet" href="style.css">
  <!-- end CSS-->

</head>

<body>

  <div class="container">
    <div id="main" role="main">

      <div id="vis"></div>
    </div>
  </div> <!--! end of #container -->


  <!-- <script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script> -->


  <script src="coffee-script.js"></script>
  <script src="d3.js"></script>
  <script src="queue.min.js"></script>
  <script src="complex.js"></script>
  <script type="text/coffeescript" src="vis.coffee"></script>


  <!--[if lt IE 7 ]>
    <script src="//ajax.googleapis.com/ajax/libs/chrome-frame/1.0.3/CFInstall.min.js"></script>
    <script>window.attachEvent('onload',function(){CFInstall.check({mode:'overlay'})})</script>
  <![endif]-->

</body>
</html>

CustomTooltip.js

function CustomTooltip(tooltipId, width){
	var tooltipId = tooltipId;
	$("body").append("<div class='tooltip' id='"+tooltipId+"'></div>");
	
	if(width){
		$("#"+tooltipId).css("width", width);
	}
	
	hideTooltip();
	
	function showTooltip(content, event){
		$("#"+tooltipId).html(content);
		$("#"+tooltipId).show();
		
		updatePosition(event);
	}
	
	function hideTooltip(){
		$("#"+tooltipId).hide();
	}
	
	function updatePosition(event){
		var ttid = "#"+tooltipId;
		var xOffset = 20;
		var yOffset = 10;
		
		 var ttw = $(ttid).width();
		 var tth = $(ttid).height();
		 var wscrY = $(window).scrollTop();
		 var wscrX = $(window).scrollLeft();
		 var curX = (document.all) ? event.clientX + wscrX : event.pageX;
		 var curY = (document.all) ? event.clientY + wscrY : event.pageY;
		 var ttleft = ((curX - wscrX + xOffset*2 + ttw) > $(window).width()) ? curX - ttw - xOffset*2 : curX + xOffset;
		 if (ttleft < wscrX + xOffset){
		 	ttleft = wscrX + xOffset;
		 } 
		 var tttop = ((curY - wscrY + yOffset*2 + tth) > $(window).height()) ? curY - tth - yOffset*2 : curY + yOffset;
		 if (tttop < wscrY + yOffset){
		 	tttop = curY + yOffset;
		 } 
		 $(ttid).css('top', tttop + 'px').css('left', ttleft + 'px');
	}
	
	return {
		showTooltip: showTooltip,
		hideTooltip: hideTooltip,
		updatePosition: updatePosition
	}
}

queue.min.js

(function(){function n(n){function t(){for(;f=a<c.length&&n>p;){var u=a++,t=c[u],r=l.call(t,1);r.push(e(u)),++p,t[0].apply(null,r)}}function e(n){return function(u,l){--p,null==d&&(null!=u?(d=u,a=s=0/0,r()):(c[n]=l,--s?f||t():r()))}}function r(){null!=d?v(d):i?v(d,c):v.apply(null,[d].concat(c))}var o,f,i,c=[],a=0,p=0,s=0,d=null,v=u;return n||(n=1/0),o={defer:function(){return d||(c.push(arguments),++s,t()),o},await:function(n){return v=n,i=!1,s||r(),o},awaitAll:function(n){return v=n,i=!0,s||r(),o}}}function u(){}"undefined"==typeof module?self.queue=n:module.exports=n,n.version="1.0.4";var l=[].slice})();

style.css

body {
}

h1 {
  /* position:absolute; */
  font-size: 44px;
  float:left;
  clear:none;
  display:inline;
  width: 40%;
  font-family: 'Open Sans Condensed', sans-serif;
  text-transform:uppercase;
}

#vis {
  min-height: 800px;
}

.active {
  stroke: orange;
}

#text_select {
  float:right;
  margin-top: 10px;
}

.tooltip {
	position: absolute;
	top: 100px;
	left: 100px;
  -moz-border-radius:5px;
	border-radius: 5px;
  border: 2px solid #000;
	/* background: #222222; */
  background: #fff;
	opacity: .9;
	/* color: #eeeeee; */
  color: black;
	padding: 10px;
	width: 300px;
	font-size: 12px;
	z-index: 10;
}

.tooltip .title {
	font-size: 18px;
}

.tooltip .name {
  font-weight:bold;
}

#footer p {
  text-align: center;
}

vis.coffee


root = exports ? this

Plot = () ->
  width = 1040
  height = 1490
  data = []
  lines = null
  margin = {top: 5, right: 5, bottom: 5, left: 5}
  xScale = d3.scale.linear().domain([0,10]).range([0,width])
  yScale = d3.scale.linear().domain([0,10]).range([height,0])
  xValue = (d) -> parseFloat(d.x)
  yValue = (d) -> parseFloat(d.y)
  mColor = "steelblue"


  chart = (selection) ->
    selection.each (rawData) ->

      data = rawData
      x1Extent = d3.extent(data, (d) -> d.x1)
      x2Extent = d3.extent(data, (d) -> d.x2)
      xScale.domain([Math.min(x1Extent[0],x2Extent[0]), Math.max(x1Extent[1], x2Extent[1])])

      y1Extent = d3.extent(data, (d) -> d.y1)
      y2Extent = d3.extent(data, (d) -> d.y2)
      yScale.domain([Math.min(y1Extent[0],y2Extent[0]), Math.max(y1Extent[1], y2Extent[1])])

      svg = d3.select(this).selectAll("svg").data([data])
      gEnter = svg.enter().append("svg").append("g")

      svg.attr("width", width + margin.left + margin.right )
      svg.attr("height", height + margin.top + margin.bottom )

      g = svg.select("g")
        .attr("transform", "translate(#{margin.left},#{margin.top})")

      lines = g.append("g").attr("id", "vis_points")
      update()

  update = () ->
    lE = lines.selectAll(".line")
      .data(data).enter()
      .append("path")
      .attr("class", "line")
      .attr("stroke", mColor)
      .attr("stroke-width", 5)
      .attr("stroke-linecap", "round")
      .attr('opacity', 0)
      .attr("d", (d) -> "M#{xScale(d.x1)},#{yScale(d.y1)}L#{xScale(d.x2)},#{yScale(d.y2)}")

    lE.transition()
      .duration(1000)
      .delay((d, i) -> i * 40 )
      .attr('opacity', 1.0)

  chart.height = (_) ->
    if !arguments.length
      return height
    height = _
    chart

  chart.width = (_) ->
    if !arguments.length
      return width
    width = _
    chart

  chart.margin = (_) ->
    if !arguments.length
      return margin
    margin = _
    chart

  chart.x = (_) ->
    if !arguments.length
      return xValue
    xValue = _
    chart

  chart.color = (_) ->
    if !arguments.length
      return mColor
    mColor = _
    chart

  chart.y = (_) ->
    if !arguments.length
      return yValue
    yValue = _
    chart

  return chart

root.Plot = Plot

root.plotData = (selector, data, plot) ->
  d3.select(selector)
    .datum(data)
    .call(plot)

# quick hack to split sentences. most of the hard work came from:
# http://stackoverflow.com/questions/11166195/regex-that-splits-long-text-in-separate-sentences-with-match
sentenceLengths = (text) ->
  text = text.replace(/['\"\‘\’]/gm,"")
  tregex = /\n|([^\r\n.!?]+([.!?]+|$))/gim
  sentences = text.match(tregex).map((s) -> s.trim())
  data = []
  sentences.forEach (s) ->
    d = {}
    d.sentence = s
    d.length = s.length
    data.push(d)
  data = data.filter (d) -> d.length > 3
  data



# real hacky way to copy the R code from original into Javascript.
# original: http://www.r-bloggers.com/sentence-drawing-function-vs-art/
# depends on complex.js library I found:
# https://github.com/patrickroberts/Javascript-Complex-Math-Library
findPositions = (data, lengthAttribute = "length", turn = -Math.PI / 2.0) ->
  one = Complex(0,1)
  currentTurn = turn
  currentPos = Complex["0"]
  currentX = 0
  currentY = 0

  data.forEach (d) ->
    d[lengthAttribute] = +(d[lengthAttribute])
    d.facing = (Math.PI / 2.0) + currentTurn
    currentTurn += turn
    mult = one.mult(Complex(d.facing,0))
    mult = Complex(0,mult.i)
    imgExp = Complex.exp(mult)
    d.move = Complex(d[lengthAttribute],0).mult(imgExp)
    currentPos = currentPos.add(d.move)
    d.pos = currentPos
    d.x2 = Math.round(d.pos.re)
    d.y2 = Math.round(d.pos.i)
    d.x1 = currentX
    d.y1 = currentY
    currentX = d.x2
    currentY = d.y2

  data

texts = {
  'gatsby':{'title':'The Great Gatsby', 'file':'great_gatsby.txt', 'color':'#D1A145'}
  'brave':{'title':'Brave New World', 'file':'brave_new_world.txt', 'color':'#70A4F2'}
  'rye':{'title':'The Catcher in the Rye', 'file':'rye.txt', 'color':'#7B5749'}
  'room':{'title':'A Room of One\'s Own', 'file':'room.txt', 'color':'#95B6E8'}
  'farewell':{'title':'A Farewell to Arms', 'file':'farewell.txt', 'color':'#657782'}
  '1984':{'title':'Nineteen Eighty-Four', 'file':'1984.txt', 'color':'#DF4C42'}
}

setupText = (text) ->
  d3.select("#name").html(text.title)

id = '1984'
current = texts[id]

plot = Plot()
display = (error, text) ->
  setupText(current)
  plot.color(current.color)
  data = sentenceLengths(text)
  convertedData = findPositions(data)
  plotData("#vis", convertedData, plot)

queue()
  .defer(d3.text, "#{current.file}")
  .await(display)