<?xml version="1.0"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
         "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink"
     width="3000"
     height="2000"
      version="1.1"
     onload="init();">
  <defs>
    <style type="text/css"><![CDATA[
    ]]></style>
  </defs>


<script type="text/javascript"><![CDATA[

  // -------------------  globals ------------------- 
  var SVG_NS ="http://www.w3.org/2000/svg";
  var canvas = null;
  const dblPdlA_freq = 220;
  //const dblPdlA_freq = 27.5000;
  const top_freq = 14080;
  var notesPerOctave_int = 31;
  var overtones = 32;
  var freqLattice_2a = [];
  var leftEdge = 40;
  var topEdge = 80;
  var rightEdge = 2020;
  var vSpacing = 10;
  var pixelsPerOctave = 200;
  var circles_a = [];
  var intervalGridLines_a = [];
  var fundamentalCount = 48;
  var detailFloater = null;
  var interface_o = {};
  var canvas = null;
  var selectedCircle = null;
  var fundamentalsLong_a = [];
  var intervals_o = {
    "2/1":{value:2/1, numerator:2 , denominator:1 , limit:2 , name:"Octave", connnectorHeight:0},
    "15/8":{value:15/8,  numerator:15 , denominator:8 , limit:5 ,name:"M. Seventh", connnectorHeight:10},
    "9/5":{value:9/5, numerator:9 , denominator:5 , limit:5 , name:"m. Seventh", connnectorHeight:20},
    "16/9":{value:16/9,  numerator:16 , denominator:9 , limit:3 ,name:"m. Seventh", connnectorHeight:30},
    "7/4":{value:7/4, numerator:7 , denominator:4 , limit:7 , name:"H. Seventh", connnectorHeight:40},
    "12/7":{value:12/7,  numerator:12 , denominator:7 , limit:7 ,name:"", connnectorHeight:50},
    "5/3":{value:5/3, numerator:5 , denominator:3 , limit:3 , name:"M. Sixth", connnectorHeight:60},
    "8/5":{value:8/5, numerator:8 , denominator:5 , limit:5 , name:"m. Sixth", connnectorHeight:70},
    "3/2":{value:3/2, numerator:3 , denominator:2 , limit:3 , name:"P. Fifth", connnectorHeight:80},
    "10/7":{value:10/7,  numerator:10 , denominator:7 , limit:7 ,name:"", connnectorHeight:90},
    "7/5":{value:7/5, numerator:7 , denominator:5 , limit:7 , name:"", connnectorHeight:100},
    "4/3":{value:4/3, numerator:4 , denominator:3 , limit:4 , name:"P. Fourth", connnectorHeight:110},
    "9/7":{value:9/7,  numerator:9 , denominator:7 , limit:7 ,name:"", connnectorHeight:120},
    "5/4":{value:5/4, numerator:5 , denominator:4 , limit:5 , name:"M. Third", connnectorHeight:130},
    "6/5":{value:6/5, numerator:6 , denominator:5 , limit:5 , name:"m. Third", connnectorHeight:140},
    "7/6":{value:7/6,  numerator:7 , denominator:6 , limit:7 ,name:"", connnectorHeight:150},
    "8/7":{value:8/7,  numerator:8 , denominator:7 , limit:7 ,name:"", connnectorHeight:160},
    "9/8":{value:9/8,  numerator:9 , denominator:8 , limit:3 ,name:"M. Tone", connnectorHeight:170},
    "10/9":{value:10/9,  numerator:10 , denominator:9 , limit:5 ,name:"m. Tone", connnectorHeight:180},
  }
  
  // ------------------- utils -------------------
  
  function setAttributes(obj, att_a){
    for(var ai=0; ai<att_a.length; ai++){
      obj.setAttribute(att_a[ai][0],att_a[ai][1]);
    }
  }
  
  function makeColor(num, den, error) { // receive numerator, denominator, error of interval
    error = Math.abs(error);
    var hex_a = ["00","11","22","33","44","55","66","77","88","99","aa","bb","cc","dd","ee","ff","ff"];
    var red_str = "00";
    var green_str = hex_a[num];
    var blue_str = hex_a[den];
    var color_str = "#" + red_str + green_str + blue_str;
    return color_str;
  }
  
  // ------------------- structural elements -------------------
  
  function Button(x,y,w,h,color_str, action_ref) {
    this.button = document.createElementNS( SVG_NS, "rect" );
    setAttributes(this.button, [
        ["x",x],
        ["y",y],
        ["width",w],
        ["height",h],
        ["fill",color_str],
        ["stroke","#333333"],
        ["stroke-width",".25"]
      ]
    )
    if(action_ref){
      this.button.addEventListener("click", action_ref,true);
    }
  }
  
  function hToggle(){
    if(interface_o.hfSelector.active != "h"){
      interface_o.hfSelector.active = "h";
      // set button colors
      interface_o.hfSelector.fButton.setAttribute("fill", "#ffffff")
      interface_o.hfSelector.hButton.setAttribute("fill", "#66cc99")
      // hide fundamental grid
      removeFundamentalGridLines();
      //makeIntervalGridLines();
      if(selectedCircle){
        dotClick(selectedCircle)
      }
    }
  }
  
  function fToggle(){
    if(interface_o.hfSelector.active != "f"){
      interface_o.hfSelector.active = "f";
      // set button colors
      interface_o.hfSelector.hButton.setAttribute("fill", "#ffffff")
      interface_o.hfSelector.fButton.setAttribute("fill", "#66cc99")
      // hide harmonic grid
      removeIntervalGridLines();
      for(var ci=0; ci<circles_a.length; ci++){
        circles_a[ci].setAttribute( "fill",   "#ffffff"); // reset color;
      }
      // display fundamental grid
      makeFundamentalGridLines();
      // clear dot color
      
      for(var ci=0; ci<circles_a.length; ci++){ // loop through circles
        var circleFreq = circles_a[ci].freq;
        var fMatch_a = false;
        for(var fi=0; fi<fundamentalsLong_a.length; fi++){
          var fundFreq = fundamentalsLong_a[fi];
          var cents =  1200* Math.log( circleFreq/fundFreq ) / Math.log (2); // calculate cents
          if(cents >= -6 && cents <= 6){  // if cents between -6 and 6;
            var fMatch_a = [fi, cents];
            break;
          }
        }
        if(fMatch_a!=false){
          var error_cents = fMatch_a[1];
          error_cents = Math.floor(Math.abs(error_cents));
          var color_str = makeColor(16-(error_cents * 2), 3, 0); // get proper color
          circles_a[ci].setAttribute( "fill",  color_str); // reset color;
          circles_a[ci].err = error_cents + " cents";
        }else{
          circles_a[ci].setAttribute( "fill",   "#ffffff"); // reset color;
          circles_a[ci].err = "n/a";
        }
      }
    }
  }
  
  function makeHFSelector() {
    interface_o.hfSelector = document.createElementNS( SVG_NS, "g" );
    interface_o.hfSelector.active = "h";
    interface_o.hfSelector.hButton = interface_o.hfSelector.appendChild(new Button(180,20,10,10,"#66cc99", hToggle).button);
    interface_o.hfSelector.hLabel = interface_o.hfSelector.appendChild(document.createElementNS( SVG_NS, "text" ));
    setAttributes(interface_o.hfSelector.hLabel, 
      [
        ["x",194],
        ["y",28],
        ["font-family", "Verdana"],
        ["font-size", "10"]
      ]
    )
    interface_o.hfSelector.fButton = interface_o.hfSelector.appendChild(new Button(180,35,10,10,"#ffffff", fToggle).button);
    interface_o.hfSelector.hLabel.appendChild(document.createTextNode("Show intersection of tones and harmonies (click on any tone below)"))
    interface_o.hfSelector.hLabel = interface_o.hfSelector.appendChild(document.createElementNS( SVG_NS, "text" ));
    setAttributes(interface_o.hfSelector.hLabel, 
      [
        ["x",194],
        ["y",43],
        ["font-family", "Verdana"],
        ["font-size", "10"]
      ]
    )
    interface_o.hfSelector.hLabel.appendChild(document.createTextNode("Show intersection of tones and fundamentals"))
    canvas.appendChild( interface_o.hfSelector );
  }
  
  function makeTonesPerOctaveSelector(){
    var group = document.createElementNS( SVG_NS, "g" );
    interface_o.tonesSelector = {}; 
    interface_o.tonesSelector.leftButton = new Button(24,25,20,20,"#66cc99",TPODecrease)
    group.appendChild( interface_o.tonesSelector.leftButton.button );
    interface_o.tonesSelector.rightButton = new Button(74,25,20,20,"#66cc99",TPOIncrease)
    group.appendChild( interface_o.tonesSelector.rightButton.button );
    var minus_txt = document.createElementNS( SVG_NS, "text" );
    setAttributes(minus_txt, [
        ["x",30],
        ["y",40],
        ["font-family", "Verdana"],
        ["font-size", "16"],
        ["stroke","#ffffff"]
      ]
    )
    minus_txt.appendChild(document.createTextNode("-"));
    minus_txt.addEventListener("click", TPODecrease,true);
    group.appendChild( minus_txt );
    var plus_txt = document.createElementNS( SVG_NS, "text" );
    setAttributes(plus_txt, [
        ["x",78],
        ["y",40],
        ["font-family", "Verdana"],
        ["font-size", "16"],
        ["stroke","#ffffff"]
      ]
    )
    plus_txt.appendChild(document.createTextNode("+"));
    minus_txt.addEventListener("click", TPOIncrease,true);
    group.appendChild( plus_txt );
    interface_o.tonesSelector.countText = document.createElementNS( SVG_NS, "text" );
    setAttributes(interface_o.tonesSelector.countText, [
        ["x",49],
        ["y",41],
        ["font-family", "Verdana"],
        ["font-size", "16"],
        ["stroke","#666666"]
      ]
    )
    interface_o.tonesSelector.countText.appendChild(document.createTextNode(notesPerOctave_int));
    group.appendChild( interface_o.tonesSelector.countText );
    var label = document.createElementNS( SVG_NS, "text" );
    setAttributes(label, [
        ["x",21],
        ["y",19],
        ["font-family", "Verdana"],
        ["font-size", "10"],
        ["stroke","#666666"]
      ]
    )
    label.appendChild(document.createTextNode("Tones Per Octave"));
    group.appendChild( label );
    canvas.appendChild( group );
  }
  
  function TPOIncrease(){
    var old_g = document.getElementById( "intervals-horiz" );
    if(old_g!=null){
      canvas.removeChild(old_g)
    }
    interface_o.tonesSelector.countText.firstChild.nodeValue = new String(++notesPerOctave_int);
    renderInterface();
  }
  function TPODecrease(){
    var old_g = document.getElementById( "intervals-horiz" );
    if(old_g!=null){
      canvas.removeChild(old_g)
    }
    interface_o.tonesSelector.countText.firstChild.nodeValue = new String(--notesPerOctave_int);
    renderInterface();
  }
  
  function makeGridLines() {
    var group = document.createElementNS( SVG_NS, "g" );
    setAttributes(group, [
        ["id", "grid-horiz"],
        ["stroke", "#999999"],
        ["stroke-width", ".25"]
      ]
    )
    for( var oi=0; oi< overtones; oi++){
      var hLine = document.createElementNS( SVG_NS, "line" );
      setAttributes(hLine, [
          ["x1", leftEdge],
          ["y1", topEdge + (oi*vSpacing)],
          ["x2", rightEdge],
          ["y2", topEdge + (oi*vSpacing)]
        ]
      )
      group.appendChild( hLine )
      var hLineLabel = document.createElementNS( SVG_NS, "text" );
      hLineLabel.setAttribute( "x", leftEdge  - 18);
      hLineLabel.setAttribute( "y", topEdge + (oi*vSpacing) + 4);
      hLineLabel.setAttribute( "font-family", "Verdana");
      hLineLabel.setAttribute( "font-size", "10");
      hLineLabel.setAttribute( "fill", "black");
      hLineLabel.appendChild(document.createTextNode(oi));
      group.appendChild( hLineLabel );
    }
    canvas.appendChild( group );
    var group = document.createElementNS( SVG_NS, "g" );
    group.setAttribute( "id", "grid-vert" );
    group.setAttribute( "stroke", "#999999" );
    group.setAttribute( "stroke-width", ".25" );
    //notesPerOctave_int
    var fi = 0;
    for(var freq=dblPdlA_freq; freq<= top_freq; freq=freq*2){
      var vLine = document.createElementNS( SVG_NS, "line" );
      vLine.setAttribute( "x1", leftEdge + (fi*pixelsPerOctave));
      vLine.setAttribute( "y1", topEdge);
      vLine.setAttribute( "x2", leftEdge + (fi*pixelsPerOctave));
      vLine.setAttribute( "y2", topEdge + (oi*vSpacing));
      group.appendChild( vLine );
      var vLineLabel = document.createElementNS( SVG_NS, "text" );
      vLineLabel.setAttribute( "x", leftEdge + (fi*pixelsPerOctave) - 10);
      vLineLabel.setAttribute( "y", topEdge - 10);
      //vLineLabel.setAttribute( "y", topEdge + (oi*vSpacing)+(2*vSpacing));
      vLineLabel.setAttribute( "font-family", "Verdana");
      vLineLabel.setAttribute( "font-size", "10");
      vLineLabel.setAttribute( "fill", "black");
      var vLineLabelVal = freq.toString() + " Hz";
      vLineLabel.appendChild(document.createTextNode(vLineLabelVal));
      group.appendChild( vLineLabel );
      fi++;
    }
    canvas.appendChild( group );
  }
  
  function makeDots (){
    for(var ci=0; ci<circles_a.length; ci++){
      interface_o.circles_group.removeChild( circles_a[ci] );
      delete circles_a[ci];
    }
    if(interface_o.circles_group){
      circles_a = [];
      canvas.removeChild( interface_o.circles_group );
    }
    circles_a = [];
    interface_o.circles_group = document.createElementNS( SVG_NS, "g" );
    for(var fi=0; fi<freqLattice_2a.length; fi++){
      var oSeries = freqLattice_2a[fi];
      for(var oi=0; oi<oSeries.length; oi++){
        var freq = oSeries[oi];
        var freqLog =  Math.log( freq/dblPdlA_freq ) / Math.log (2)  * pixelsPerOctave ;
        var dot = document.createElementNS( SVG_NS, "circle" );
        dot.setAttribute( "cx",  freqLog + leftEdge);
        dot.setAttribute( "cy",  topEdge + (oi*vSpacing));
        dot.setAttribute( "r",  "3");
        dot.setAttribute( "fill",  "#ffffff");
        dot.setAttribute( "stroke",  "#aaaaaa");
        dot.fi = fi;
        dot.freq = freq;
        dot.overtone = oi;
        dot.err = "n/a";
        dot.addEventListener("mouseover", dotMouseOver,true);
        dot.setAttribute( "onmouseout", 'dotMouseOut(this);' );
        dot.setAttribute( "onclick", 'dotClick(this);' );
        circles_a.push(dot);
        interface_o.circles_group.appendChild(dot);
      }
    }
    canvas.appendChild( interface_o.circles_group );
  }
  
  function removeFundamentalGridLines(){
    if(interface_o.hasOwnProperty("fundGridLines_group")){
      while(interface_o.fundGridLines_group.hasChildNodes()){
        interface_o.fundGridLines_group.removeChild(interface_o.fundGridLines_group.firstChild);
      }
      canvas.removeChild(interface_o.fundGridLines_group);
    }
  }
  
  function makeFundamentalGridLines(){
    fundamentalsLong_a = [];
    var newKey_freq = dblPdlA_freq;
    var key_int = 0;
    while( newKey_freq < top_freq ){
      var newKey_freq = dblPdlA_freq * Math.pow(2, (key_int / notesPerOctave_int ));
      fundamentalsLong_a.push(newKey_freq);
      key_int++;
    }
    interface_o.fundGridLines_group = canvas.appendChild(document.createElementNS( SVG_NS, "g" ));
    for(var fi=0; fi<fundamentalsLong_a.length; fi++){
      var freq = fundamentalsLong_a[fi];
      var freqLog =  Math.log( freq/dblPdlA_freq ) / Math.log (2)  * pixelsPerOctave ;
      var vLine = interface_o.fundGridLines_group.appendChild(document.createElementNS( SVG_NS, "line" ));
      setAttributes(
        vLine,
        [
          ["x1",  freqLog + leftEdge],
          [ "y1", topEdge],
          ["x2",  freqLog + leftEdge],
          ["y2", topEdge + (overtones*vSpacing)],
          ["stroke", "#000000"],
          ["opacity", ".1"]
        ]
      )
      
    }
    
    
  }
  
  function displayIntervalGridLines(){
    if(interface_o.hasOwnProperty("intervalGridLines_g")){
      canvas.appendChild(interface_o.intervalGridLines_g)
    }
  }
  
  function removeIntervalGridLines(){
    if(interface_o.hasOwnProperty("intervalGridLines_g")){
      if(interface_o.intervalGridLines_g.parentNode!=null){
        canvas.removeChild(interface_o.intervalGridLines_g)
      }
    }
  }
  
  function makeIntervalGridLines(){
    removeIntervalGridLines();
    interface_o.intervalGridLines_g = document.createElementNS( SVG_NS, "g" );
    interface_o.intervalGridLines_g.setAttribute( "id", "intervals-horiz" );
    interface_o.intervalGridLines_g.setAttribute( "stroke", "#993300" );
    interface_o.intervalGridLines_g.setAttribute( "stroke-width", ".25" );
    for(var iName in intervals_o){ // cycle through intervals
      var iFreq = intervals_o[iName].value * selectedCircle.freq;
      var vLine = document.createElementNS( SVG_NS, "line" );
      var freqLog =  Math.log( iFreq/dblPdlA_freq ) / Math.log (2)  * pixelsPerOctave ;
      vLine.setAttribute( "x1",  freqLog + leftEdge );
      vLine.setAttribute( "y1", topEdge);
      vLine.setAttribute( "x2",  freqLog + leftEdge);
      vLine.setAttribute( "y2", topEdge + (overtones*vSpacing) + intervals_o[iName].connnectorHeight);
      interface_o.intervalGridLines_g.appendChild( vLine );
      var vLineLabel = document.createElementNS( SVG_NS, "text" );
      vLineLabel.setAttribute( "x", freqLog + leftEdge);
      vLineLabel.setAttribute( "y", topEdge + (overtones*vSpacing) + intervals_o[iName].connnectorHeight + 10);
      vLineLabel.setAttribute( "font-family", "Verdana");
      vLineLabel.setAttribute( "font-size", "10");
      vLineLabel.setAttribute( "fill", "black");
      vLineLabel.appendChild(document.createTextNode(iName + " (" + intervals_o[iName].name + ") "));
      intervals_o[iName].labelVal = vLineLabel.appendChild(document.createTextNode("0"));
      interface_o.intervalGridLines_g.appendChild( vLineLabel );
    }
    canvas.appendChild( interface_o.intervalGridLines_g );
  }
  
  function makeDetailFloater(){
    interface_o.detailFloaterGroup = document.createElementNS( SVG_NS, "g" );
    interface_o.detailFloaterGroup.setAttribute("visibility","hidden");
    interface_o.detailFloaterGroup.detailFloater = interface_o.detailFloaterGroup.appendChild(document.createElementNS( SVG_NS, "rect" ));
    setAttributes(interface_o.detailFloaterGroup.detailFloater ,  
      [
        ["x","10"],
        ["y","10"],
        ["width","120"],
        ["height","60"],
        ["fill","white"],
        ["stroke","#777777"],
        ["stroke-width","1"],
        ["opacity",".8"]
      ]
    )
    interface_o.detailFloaterGroup.labels = {}
    interface_o.detailFloaterGroup.vals = {}
    
    interface_o.detailFloaterGroup.labels.freq = document.createElementNS( SVG_NS, "text" );
    setAttributes(interface_o.detailFloaterGroup.labels.freq ,
      [["font-family", "Verdana"], ["font-size", "10"]]
    );
    interface_o.detailFloaterGroup.labels.freq.appendChild(document.createTextNode("freq:"));
    interface_o.detailFloaterGroup.appendChild(interface_o.detailFloaterGroup.labels.freq);
    interface_o.detailFloaterGroup.vals.freq = document.createElementNS( SVG_NS, "text" );
    setAttributes(interface_o.detailFloaterGroup.vals.freq ,
      [["font-family", "Verdana"],["font-size", "10"]]
    );
    interface_o.detailFloaterGroup.vals.freq.appendChild(document.createTextNode(":"));
    interface_o.detailFloaterGroup.appendChild(interface_o.detailFloaterGroup.vals.freq);
    
    interface_o.detailFloaterGroup.labels.fund = document.createElementNS( SVG_NS, "text" );
    setAttributes(interface_o.detailFloaterGroup.labels.fund ,
      [["font-family", "Verdana"], ["font-size", "10"]]
    );
    interface_o.detailFloaterGroup.labels.fund.appendChild(document.createTextNode("fundamental:"));
    interface_o.detailFloaterGroup.appendChild(interface_o.detailFloaterGroup.labels.fund);
    interface_o.detailFloaterGroup.vals.fund = document.createElementNS( SVG_NS, "text" );
    setAttributes(interface_o.detailFloaterGroup.vals.fund ,
      [["font-family", "Verdana"],["font-size", "10"]]
    );
    interface_o.detailFloaterGroup.vals.fund.appendChild(document.createTextNode(":"));
    interface_o.detailFloaterGroup.appendChild(interface_o.detailFloaterGroup.vals.fund);
    
    interface_o.detailFloaterGroup.labels.over = document.createElementNS( SVG_NS, "text" );
    setAttributes(interface_o.detailFloaterGroup.labels.over ,
      [["font-family", "Verdana"], ["font-size", "10"]]
    );
    interface_o.detailFloaterGroup.labels.over.appendChild(document.createTextNode("overtone:"));
    interface_o.detailFloaterGroup.appendChild(interface_o.detailFloaterGroup.labels.over);
    interface_o.detailFloaterGroup.vals.over = document.createElementNS( SVG_NS, "text" );
    setAttributes(interface_o.detailFloaterGroup.vals.over ,
      [["font-family", "Verdana"],["font-size", "10"]]
    );
    interface_o.detailFloaterGroup.vals.over.appendChild(document.createTextNode(":"));
    interface_o.detailFloaterGroup.appendChild(interface_o.detailFloaterGroup.vals.over);
    
    
    interface_o.detailFloaterGroup.labels.err = document.createElementNS( SVG_NS, "text" );
    setAttributes(interface_o.detailFloaterGroup.labels.err ,
      [["font-family", "Verdana"], ["font-size", "10"]]
    );
    interface_o.detailFloaterGroup.labels.err.appendChild(document.createTextNode("error:"));
    interface_o.detailFloaterGroup.appendChild(interface_o.detailFloaterGroup.labels.err);
    interface_o.detailFloaterGroup.vals.err = document.createElementNS( SVG_NS, "text" );
    setAttributes(interface_o.detailFloaterGroup.vals.err ,
      [["font-family", "Verdana"],["font-size", "10"]]
    );
    interface_o.detailFloaterGroup.vals.err.appendChild(document.createTextNode(":"));
    interface_o.detailFloaterGroup.appendChild(interface_o.detailFloaterGroup.vals.err);
    
    canvas.appendChild( interface_o.detailFloaterGroup );
  }
  // ------------------- tonal math  ------------------- 
  
  function makeFundamentals(fCount){
    var keyFreq_a = [];
    for(var key_int=0; key_int<=fCount; key_int++){
      var newKey_freq = dblPdlA_freq * Math.pow(2, (key_int / notesPerOctave_int ));
      keyFreq_a.push(newKey_freq);
    }
    return keyFreq_a
  }
  
  function makeOvertones(freq_float, steps_int){
    var h_a = [freq_float];
    var harmonicSum_int = 0;
    for(var step=1; step<steps_int; step++){
      harmonicSum_int += 1/step;
      //console.log(1/step)
      h_a.push((1+step) * freq_float);
      //h_a.push((1+harmonicSum_int) * freq_float);
    }
    return h_a;
  }
  
  function calcIntervalMatch(freq, cFreq){  // compare the difference between the clicked freq and another circle's freq to the ideal just intervals
    for(var iName in intervals_o){ // cycle through intervals
      var iFreq = intervals_o[iName].value * freq; // generate iFreq value;
      var cents =  1200* Math.log( cFreq/iFreq ) / Math.log (2); // calculate cents
      if(cents >= -6 && cents <= 6){  // if cents between -6 and 6;
        return [iName, cents];
      }
    }
    return false
  }
  
  // -------------------  event handlers ------------------- 
  
  function dotMouseOver( event ) {
    var circle_ref = event.currentTarget;
    var mouseX_int = event.layerX + 10;
    var mouseY_int = event.layerY - 70;
    for(var ci=0; ci<circles_a.length; ci++){
      if(circles_a[ci].fi == circle_ref.fi){
        circles_a[ci].setAttribute( "stroke",  "#ffaaaa");
      }else{
        circles_a[ci].setAttribute( "stroke",  "#aaaaaa");
      }
    }
    circle_ref.setAttribute( 'r', '5' );
    
    interface_o.detailFloaterGroup.setAttribute("visibility","visible");
    interface_o.detailFloaterGroup.detailFloater.setAttribute( 'x', mouseX_int );
    interface_o.detailFloaterGroup.detailFloater.setAttribute( 'y', mouseY_int );
    
    setAttributes(interface_o.detailFloaterGroup.labels.freq,  [ ['x', mouseX_int + 2], ['y', mouseY_int + 10] ]  )
    setAttributes(interface_o.detailFloaterGroup.vals.freq,  [ ['x', mouseX_int + 32], ['y', mouseY_int + 10] ]  )
    var freqVal = new String(Math.round(circle_ref.freq * 1000) / 1000) + "Hz";
    interface_o.detailFloaterGroup.vals.freq.firstChild.nodeValue = new String(freqVal);
    
    setAttributes(interface_o.detailFloaterGroup.labels.fund,  [ ['x', mouseX_int + 2], ['y', mouseY_int + 22] ]  )
    setAttributes(interface_o.detailFloaterGroup.vals.fund,  [ ['x', mouseX_int + 82], ['y', mouseY_int + 22] ]  )
    var fundVal = new String((circle_ref.fi % notesPerOctave_int) + "/" + notesPerOctave_int)
    interface_o.detailFloaterGroup.vals.fund.firstChild.nodeValue = new String(fundVal);
    
    setAttributes(interface_o.detailFloaterGroup.labels.over,  [ ['x', mouseX_int + 2], ['y', mouseY_int + 34] ]  )
    setAttributes(interface_o.detailFloaterGroup.vals.over,  [ ['x', mouseX_int + 62], ['y', mouseY_int + 34] ]  )
    var overVal = new String(circle_ref.overtone);
    interface_o.detailFloaterGroup.vals.over.firstChild.nodeValue = new String(overVal);
    
    setAttributes(interface_o.detailFloaterGroup.labels.err,  [ ['x', mouseX_int + 2], ['y', mouseY_int + 46] ]  )
    setAttributes(interface_o.detailFloaterGroup.vals.err,  [ ['x', mouseX_int + 38], ['y', mouseY_int + 46] ]  )
    var errVal = new String(circle_ref.err);
    interface_o.detailFloaterGroup.vals.err.firstChild.nodeValue = new String(errVal);
  }

  function dotMouseOut( circle_ref ) {
      circle_ref.setAttribute( 'r', '3' );
      interface_o.detailFloaterGroup.setAttribute("visibility","hidden");
  }
  
  function dotClick( circle_ref ) {
    // reset interval counts
    for(var iName in intervals_o){ // cycle through intervals
      intervals_o[iName].count = 0;
    }
    var freq = circle_ref.freq;
    for(var ci=0; ci<circles_a.length; ci++){
      var iMatch_a = calcIntervalMatch(freq, circles_a[ci].freq);
      if(iMatch_a!=false){
        var error_cents = iMatch_a[1];
        error_cents = Math.floor(Math.abs(error_cents));
        var color_str = makeColor(16-(error_cents * 2), 3, 0); // get proper color
        circles_a[ci].setAttribute( "fill",  color_str); // reset color;
        circles_a[ci].err = error_cents + " cents";
        intervals_o[iMatch_a[0]].count++
      }else{
        circles_a[ci].setAttribute( "fill",   "#ffffff"); // reset color;
        circles_a[ci].err = "n/a";
      }
    }
    selectedCircle = circle_ref;
    makeIntervalGridLines();
    for(var iName in intervals_o){ // cycle through intervals
      intervals_o[iName].labelVal.nodeValue = intervals_o[iName].count;
    }
    circle_ref.setAttribute( "fill",   "#ff0000"); // reset color;
  }
  
  function init() {
    canvas = document.getElementById( "top_level" ); 
    makeTonesPerOctaveSelector()
    makeHFSelector();
    makeGridLines();
    renderInterface()
  }
  
  function renderInterface(){
    var fundamentals_a = makeFundamentals(fundamentalCount);
    freqLattice_2a = [];
    for(var fi=0; fi< fundamentals_a.length; fi++){
      overtones_a = makeOvertones(fundamentals_a[fi], overtones);
      freqLattice_2a.push(overtones_a);
    }
    makeDots();
    makeDetailFloater();
  }
    
]]></script>
<g id="top_level"  />
</svg>
