<commit_before>/*
The MIT License (MIT)

Copyright (c) 2014 Chris Wilson

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

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

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

window.AudioContext = window.AudioContext || window.webkitAudioContext;

var audioContext = new AudioContext();
var isPlaying = false;
var sourceNode = null;
var analyser = null;
var theBuffer = null;
var DEBUGCANVAS = null;
var detectorElem,
	canvasElem,
	waveCanvas,
	pitchElem,
	noteElem,
	detuneElem,
	detuneAmount;

window.onload = function() {
	var request = new XMLHttpRequest();
	request.open("GET", "../sounds/whistling3.ogg", true);
	request.responseType = "arraybuffer";
	request.onload = function() {
	  audioContext.decodeAudioData( request.response, function(buffer) {
	    	theBuffer = buffer;
		} );
	}
	request.send();

	detectorElem = document.getElementById( "detector" );
	canvasElem = document.getElementById( "output" );
	DEBUGCANVAS = document.getElementById( "waveform" );
	if (DEBUGCANVAS) {
		waveCanvas = DEBUGCANVAS.getContext("2d");
		waveCanvas.strokeStyle = "black";
		waveCanvas.lineWidth = 1;
	}
	pitchElem = document.getElementById( "pitch" );
	noteElem = document.getElementById( "note" );
	detuneElem = document.getElementById( "detune" );
	detuneAmount = document.getElementById( "detune_amt" );

	detectorElem.ondragenter = function () {
		this.classList.add("droptarget");
		return false; };
	detectorElem.ondragleave = function () { this.classList.remove("droptarget"); return false; };
	detectorElem.ondrop = function (e) {
  		this.classList.remove("droptarget");
  		e.preventDefault();
		theBuffer = null;

	  	var reader = new FileReader();
	  	reader.onload = function (event) {
	  		audioContext.decodeAudioData( event.target.result, function(buffer) {
	    		theBuffer = buffer;
	  		}, function(){alert("error loading!");} );

	  	};
	  	reader.onerror = function (event) {
	  		alert("Error: " + reader.error );
		};
	  	reader.readAsArrayBuffer(e.dataTransfer.files[0]);
	  	return false;
	};

}

function error() {
    alert('Stream generation failed.');
}

function getUserMedia(dictionary, callback) {
    try {
        navigator.getUserMedia =
        	navigator.getUserMedia ||
        	navigator.webkitGetUserMedia ||
        	navigator.mozGetUserMedia;
        navigator.getUserMedia(dictionary, callback, error);
    } catch (e) {
        alert('getUserMedia threw exception :' + e);
    }
}

function gotStream(stream) {
    // Create an AudioNode from the stream.
    var mediaStreamSource = audioContext.createMediaStreamSource(stream);

    // Connect it to the destination.
    analyser = audioContext.createAnalyser();
    analyser.fftSize = 2048;
    mediaStreamSource.connect( analyser );
    updatePitch();
}

function toggleOscillator() {
    if (isPlaying) {
        //stop playing and return
        sourceNode.stop( now );
        sourceNode = null;
        analyser = null;
        isPlaying = false;
		if (!window.cancelAnimationFrame)
			window.cancelAnimationFrame = window.webkitCancelAnimationFrame;
        window.cancelAnimationFrame( rafID );
        return "play oscillator";
    }
    sourceNode = audioContext.createOscillator();

    analyser = audioContext.createAnalyser();
    analyser.fftSize = 2048;
    sourceNode.connect( analyser );
    analyser.connect( audioContext.destination );
    sourceNode.start(0);
    isPlaying = true;
    isLiveInput = false;
    updatePitch();

    return "stop";
}

function toggleLiveInput() {
    if (isPlaying) {
        //stop playing and return
        sourceNode.stop( now );
        sourceNode = null;
        analyser = null;
        isPlaying = false;
		if (!window.cancelAnimationFrame)
			window.cancelAnimationFrame = window.webkitCancelAnimationFrame;
        window.cancelAnimationFrame( rafID );
    }
    getUserMedia(
    	{
            "audio": {
                "mandatory": {
                    "googEchoCancellation": "false",
                    "googAutoGainControl": "false",
                    "googNoiseSuppression": "false",
                    "googHighpassFilter": "false"
                },
                "optional": []
            },
        }, gotStream);
}

function togglePlayback() {
    var now = audioContext.currentTime;

    if (isPlaying) {
        //stop playing and return
        sourceNode.stop( now );
        sourceNode = null;
        analyser = null;
        isPlaying = false;
		if (!window.cancelAnimationFrame)
			window.cancelAnimationFrame = window.webkitCancelAnimationFrame;
        window.cancelAnimationFrame( rafID );
        return "start";
    }

    sourceNode = audioContext.createBufferSource();
    sourceNode.buffer = theBuffer;
    sourceNode.loop = true;

    analyser = audioContext.createAnalyser();
    analyser.fftSize = 2048;
    sourceNode.connect( analyser );
    analyser.connect( audioContext.destination );
    sourceNode.start( now );
    isPlaying = true;
    isLiveInput = false;
    updatePitch();

    return "stop";
}

var rafID = null;
var tracks = null;
var buflen = 2048;
var buf = new Uint8Array( buflen );
var MINVAL = 134;  // 128 == zero.  MINVAL is the "minimum detected signal" level.

function findNextPositiveZeroCrossing( start ) {
	var i = Math.ceil( start );
	var last_zero = -1;
	// advance until we're zero or negative
	while (i<buflen && (buf[i] > 128 ) )
		i++;
	if (i>=buflen)
		return -1;

	// advance until we're above MINVAL, keeping track of last zero.
	while (i<buflen && ((t=buf[i]) < MINVAL )) {
		if (t >= 128) {
			if (last_zero == -1)
				last_zero = i;
		} else
			last_zero = -1;
		i++;
	}

	// we may have jumped over MINVAL in one sample.
	if (last_zero == -1)
		last_zero = i;

	if (i==buflen)	// We didn't find any more positive zero crossings
		return -1;

	// The first sample might be a zero.  If so, return it.
	if (last_zero == 0)
		return 0;

	// Otherwise, the zero might be between two values, so we need to scale it.

	var t = ( 128 - buf[last_zero-1] ) / (buf[last_zero] - buf[last_zero-1]);
	return last_zero+t;
}

var noteStrings = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];

function noteFromPitch( frequency ) {
	var noteNum = 12 * (Math.log( frequency / 440 )/Math.log(2) );
	return Math.round( noteNum ) + 69;
}

function frequencyFromNoteNumber( note ) {
	return 440 * Math.pow(2,(note-69)/12);
}

function centsOffFromPitch( frequency, note ) {
	return Math.floor( 1200 * Math.log( frequency / frequencyFromNoteNumber( note ))/Math.log(2) );
}

// this is a float version of the algorithm below - but it's not currently used.
/*
function autoCorrelateFloat( buf, sampleRate ) {
	var MIN_SAMPLES = 4;	// corresponds to an 11kHz signal
	var MAX_SAMPLES = 1000; // corresponds to a 44Hz signal
	var SIZE = 1000;
	var best_offset = -1;
	var best_correlation = 0;
	var rms = 0;

	if (buf.length < (SIZE + MAX_SAMPLES - MIN_SAMPLES))
		return -1;  // Not enough data

	for (var i=0;i<SIZE;i++)
		rms += buf[i]*buf[i];
	rms = Math.sqrt(rms/SIZE);

	for (var offset = MIN_SAMPLES; offset <= MAX_SAMPLES; offset++) {
		var correlation = 0;

		for (var i=0; i<SIZE; i++) {
			correlation += Math.abs(buf[i]-buf[i+offset]);
		}
		correlation = 1 - (correlation/SIZE);
		if (correlation > best_correlation) {
			best_correlation = correlation;
			best_offset = offset;
		}
	}
	if ((rms>0.1)&&(best_correlation > 0.1)) {
		console.log("f = " + sampleRate/best_offset + "Hz (rms: " + rms + " confidence: " + best_correlation + ")");
	}
//	var best_frequency = sampleRate/best_offset;
}
*/

function autoCorrelate( buf, sampleRate ) {
	var MIN_SAMPLES = 4;	// corresponds to an 11kHz signal
	var MAX_SAMPLES = 1000; // corresponds to a 44Hz signal
	var SIZE = 1000;
	var best_offset = -1;
	var best_correlation = 0;
	var rms = 0;
	var foundGoodCorrelation = false;

	if (buf.length < (SIZE + MAX_SAMPLES - MIN_SAMPLES))
		return -1;  // Not enough data

	for (var i=0;i<SIZE;i++) {
		var val = (buf[i] - 128)/128;
		rms += val*val;
	}
	rms = Math.sqrt(rms/SIZE);
	if (rms<0.01)
		return -1;

	var lastCorrelation=1;
	for (var offset = MIN_SAMPLES; offset <= MAX_SAMPLES; offset++) {
		var correlation = 0;

		for (var i=0; i<SIZE; i++) {
			correlation += Math.abs(((buf[i] - 128)/128)-((buf[i+offset] - 128)/128));
		}
		correlation = 1 - (correlation/SIZE);
		if ((correlation>0.9) && (correlation > lastCorrelation))
			foundGoodCorrelation = true;
		else if (foundGoodCorrelation) {
			// short-circuit - we found a good correlation, then a bad one, so we'd just be seeing copies from here.
			return sampleRate/best_offset;
		}
		lastCorrelation = correlation;
		if (correlation > best_correlation) {
			best_correlation = correlation;
			best_offset = offset;
		}
	}
	if (best_correlation > 0.01) {
		// console.log("f = " + sampleRate/best_offset + "Hz (rms: " + rms + " confidence: " + best_correlation + ")")
		return sampleRate/best_offset;
	}
	return -1;
//	var best_frequency = sampleRate/best_offset;
}

function updatePitch( time ) {
	var cycles = new Array;
	analyser.getByteTimeDomainData( buf );

/*
// old zero-crossing code

	var i=0;
	// find the first point
	var last_zero = findNextPositiveZeroCrossing( 0 );

	var n=0;
	// keep finding points, adding cycle lengths to array
	while ( last_zero != -1) {
		var next_zero = findNextPositiveZeroCrossing( last_zero + 1 );
		if (next_zero > -1)
			cycles.push( next_zero - last_zero );
		last_zero = next_zero;

		n++;
		if (n>1000)
			break;
	}

	// 1?: average the array
	var num_cycles = cycles.length;
	var sum = 0;
	var pitch = 0;

	for (var i=0; i<num_cycles; i++) {
		sum += cycles[i];
	}

	if (num_cycles) {
		sum /= num_cycles;
		pitch = audioContext.sampleRate/sum;
	}

// confidence = num_cycles / num_possible_cycles = num_cycles / (audioContext.sampleRate/)
	var confidence = (num_cycles ? ((num_cycles/(pitch * buflen / audioContext.sampleRate)) * 100) : 0);
*/

/*
	console.log(
		"Cycles: " + num_cycles +
		" - average length: " + sum +
		" - pitch: " + pitch + "Hz " +
		" - note: " + noteFromPitch( pitch ) +
		" - confidence: " + confidence + "% "
		);
*/
	// possible other approach to confidence: sort the array, take the median; go through the array and compute the average deviation
	var ac = autoCorrelate( buf, audioContext.sampleRate );

// 	detectorElem.className = (confidence>50)?"confident":"vague";

	// TODO: Paint confidence meter on canvasElem here.

	if (DEBUGCANVAS) {  // This draws the current waveform, useful for debugging
		waveCanvas.clearRect(0,0,512,256);
		waveCanvas.strokeStyle = "red";
		waveCanvas.beginPath();
		waveCanvas.moveTo(0,0);
		waveCanvas.lineTo(0,256);
		waveCanvas.moveTo(128,0);
		waveCanvas.lineTo(128,256);
		waveCanvas.moveTo(256,0);
		waveCanvas.lineTo(256,256);
		waveCanvas.moveTo(384,0);
		waveCanvas.lineTo(384,256);
		waveCanvas.moveTo(512,0);
		waveCanvas.lineTo(512,256);
		waveCanvas.stroke();
		waveCanvas.strokeStyle = "black";
		waveCanvas.beginPath();
		waveCanvas.moveTo(0,buf[0]);
		for (var i=1;i<512;i++) {
			waveCanvas.lineTo(i,buf[i]);
		}
		waveCanvas.stroke();
	}

 	if (ac == -1) {
 		detectorElem.className = "vague";
	 	pitchElem.innerText = "--";
		noteElem.innerText = "-";
		detuneElem.className = "";
		detuneAmount.innerText = "--";
 	} else {
	 	detectorElem.className = "confident";
	 	pitch = ac;
	 	pitchElem.innerText = Math.floor( pitch ) ;
	 	var note =  noteFromPitch( pitch );
		noteElem.innerHTML = noteStrings[note%12];
		var detune = centsOffFromPitch( pitch, note );
		if (detune == 0 ) {
			detuneElem.className = "";
			detuneAmount.innerHTML = "--";
		} else {
			if (detune < 0)
				detuneElem.className = "flat";
			else
				detuneElem.className = "sharp";
			detuneAmount.innerHTML = Math.abs( detune );
		}
	}

	if (!window.requestAnimationFrame)
		window.requestAnimationFrame = window.webkitRequestAnimationFrame;
	rafID = window.requestAnimationFrame( updatePitch );
}
<commit_msg>Move to floats, fix high sample rate problems.<commit_after>/*
The MIT License (MIT)

Copyright (c) 2014 Chris Wilson

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

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

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

window.AudioContext = window.AudioContext || window.webkitAudioContext;

var audioContext = null;
var isPlaying = false;
var sourceNode = null;
var analyser = null;
var theBuffer = null;
var DEBUGCANVAS = null;
var mediaStreamSource = null;
var detectorElem,
	canvasElem,
	waveCanvas,
	pitchElem,
	noteElem,
	detuneElem,
	detuneAmount;

window.onload = function() {
	audioContext = new AudioContext();
	MAX_SIZE = Math.max(4,Math.floor(audioContext.sampleRate/5000));	// corresponds to a 5kHz signal
	var request = new XMLHttpRequest();
	request.open("GET", "../sounds/whistling3.ogg", true);
	request.responseType = "arraybuffer";
	request.onload = function() {
	  audioContext.decodeAudioData( request.response, function(buffer) {
	    	theBuffer = buffer;
		} );
	}
	request.send();

	detectorElem = document.getElementById( "detector" );
	canvasElem = document.getElementById( "output" );
	DEBUGCANVAS = document.getElementById( "waveform" );
	if (DEBUGCANVAS) {
		waveCanvas = DEBUGCANVAS.getContext("2d");
		waveCanvas.strokeStyle = "black";
		waveCanvas.lineWidth = 1;
	}
	pitchElem = document.getElementById( "pitch" );
	noteElem = document.getElementById( "note" );
	detuneElem = document.getElementById( "detune" );
	detuneAmount = document.getElementById( "detune_amt" );

	detectorElem.ondragenter = function () {
		this.classList.add("droptarget");
		return false; };
	detectorElem.ondragleave = function () { this.classList.remove("droptarget"); return false; };
	detectorElem.ondrop = function (e) {
  		this.classList.remove("droptarget");
  		e.preventDefault();
		theBuffer = null;

	  	var reader = new FileReader();
	  	reader.onload = function (event) {
	  		audioContext.decodeAudioData( event.target.result, function(buffer) {
	    		theBuffer = buffer;
	  		}, function(){alert("error loading!");} );

	  	};
	  	reader.onerror = function (event) {
	  		alert("Error: " + reader.error );
		};
	  	reader.readAsArrayBuffer(e.dataTransfer.files[0]);
	  	return false;
	};

}

function error() {
    alert('Stream generation failed.');
}

function getUserMedia(dictionary, callback) {
    try {
        navigator.getUserMedia =
        	navigator.getUserMedia ||
        	navigator.webkitGetUserMedia ||
        	navigator.mozGetUserMedia;
        navigator.getUserMedia(dictionary, callback, error);
    } catch (e) {
        alert('getUserMedia threw exception :' + e);
    }
}

function gotStream(stream) {
    // Create an AudioNode from the stream.
    mediaStreamSource = audioContext.createMediaStreamSource(stream);

    // Connect it to the destination.
    analyser = audioContext.createAnalyser();
    analyser.fftSize = 2048;
    mediaStreamSource.connect( analyser );
    updatePitch();
}

function toggleOscillator() {
    if (isPlaying) {
        //stop playing and return
        sourceNode.stop( 0 );
        sourceNode = null;
        analyser = null;
        isPlaying = false;
		if (!window.cancelAnimationFrame)
			window.cancelAnimationFrame = window.webkitCancelAnimationFrame;
        window.cancelAnimationFrame( rafID );
        return "play oscillator";
    }
    sourceNode = audioContext.createOscillator();

    analyser = audioContext.createAnalyser();
    analyser.fftSize = 2048;
    sourceNode.connect( analyser );
    analyser.connect( audioContext.destination );
    sourceNode.start(0);
    isPlaying = true;
    isLiveInput = false;
    updatePitch();

    return "stop";
}

function toggleLiveInput() {
    if (isPlaying) {
        //stop playing and return
        sourceNode.stop( 0 );
        sourceNode = null;
        analyser = null;
        isPlaying = false;
		if (!window.cancelAnimationFrame)
			window.cancelAnimationFrame = window.webkitCancelAnimationFrame;
        window.cancelAnimationFrame( rafID );
    }
    getUserMedia(
    	{
            "audio": {
                "mandatory": {
                    "googEchoCancellation": "false",
                    "googAutoGainControl": "false",
                    "googNoiseSuppression": "false",
                    "googHighpassFilter": "false"
                },
                "optional": []
            },
        }, gotStream);
}

function togglePlayback() {
    if (isPlaying) {
        //stop playing and return
        sourceNode.stop( 0 );
        sourceNode = null;
        analyser = null;
        isPlaying = false;
		if (!window.cancelAnimationFrame)
			window.cancelAnimationFrame = window.webkitCancelAnimationFrame;
        window.cancelAnimationFrame( rafID );
        return "start";
    }

    sourceNode = audioContext.createBufferSource();
    sourceNode.buffer = theBuffer;
    sourceNode.loop = true;

    analyser = audioContext.createAnalyser();
    analyser.fftSize = 2048;
    sourceNode.connect( analyser );
    analyser.connect( audioContext.destination );
    sourceNode.start( 0 );
    isPlaying = true;
    isLiveInput = false;
    updatePitch();

    return "stop";
}

var rafID = null;
var tracks = null;
var buflen = 1024;
var buf = new Float32Array( buflen );

var noteStrings = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];

function noteFromPitch( frequency ) {
	var noteNum = 12 * (Math.log( frequency / 440 )/Math.log(2) );
	return Math.round( noteNum ) + 69;
}

function frequencyFromNoteNumber( note ) {
	return 440 * Math.pow(2,(note-69)/12);
}

function centsOffFromPitch( frequency, note ) {
	return Math.floor( 1200 * Math.log( frequency / frequencyFromNoteNumber( note ))/Math.log(2) );
}

// this is a float version of the algorithm below - but it's not currently used.
/*
function autoCorrelateFloat( buf, sampleRate ) {
	var MIN_SAMPLES = 4;	// corresponds to an 11kHz signal
	var MAX_SAMPLES = 1000; // corresponds to a 44Hz signal
	var SIZE = 1000;
	var best_offset = -1;
	var best_correlation = 0;
	var rms = 0;

	if (buf.length < (SIZE + MAX_SAMPLES - MIN_SAMPLES))
		return -1;  // Not enough data

	for (var i=0;i<SIZE;i++)
		rms += buf[i]*buf[i];
	rms = Math.sqrt(rms/SIZE);

	for (var offset = MIN_SAMPLES; offset <= MAX_SAMPLES; offset++) {
		var correlation = 0;

		for (var i=0; i<SIZE; i++) {
			correlation += Math.abs(buf[i]-buf[i+offset]);
		}
		correlation = 1 - (correlation/SIZE);
		if (correlation > best_correlation) {
			best_correlation = correlation;
			best_offset = offset;
		}
	}
	if ((rms>0.1)&&(best_correlation > 0.1)) {
		console.log("f = " + sampleRate/best_offset + "Hz (rms: " + rms + " confidence: " + best_correlation + ")");
	}
//	var best_frequency = sampleRate/best_offset;
}
*/

var MIN_SAMPLES = 0;  // will be initialized when AudioContext is created.

function autoCorrelate( buf, sampleRate ) {
	var SIZE = buf.length;
	var MAX_SAMPLES = Math.floor(SIZE/2);
	var best_offset = -1;
	var best_correlation = 0;
	var rms = 0;
	var foundGoodCorrelation = false;
	var correlations = new Array(MAX_SAMPLES);

	for (var i=0;i<SIZE;i++) {
		var val = buf[i];
		rms += val*val;
	}
	rms = Math.sqrt(rms/SIZE);
	if (rms<0.01) // not enough signal
		return -1;

	var lastCorrelation=1;
	for (var offset = MIN_SAMPLES; offset < MAX_SAMPLES; offset++) {
		var correlation = 0;

		for (var i=0; i<MAX_SAMPLES; i++) {
			correlation += Math.abs((buf[i])-(buf[i+offset]));
		}
		correlation = 1 - (correlation/MAX_SAMPLES);
		correlations[offset] = correlation; // store it, for the tweaking we need to do below.
		if ((correlation>0.9) && (correlation > lastCorrelation)) {
			foundGoodCorrelation = true;
			if (correlation > best_correlation) {
				best_correlation = correlation;
				best_offset = offset;
			}
		} else if (foundGoodCorrelation) {
			// short-circuit - we found a good correlation, then a bad one, so we'd just be seeing copies from here.
			// Now we need to tweak the offset - by interpolating between the values to the left and right of the
			// best offset, and shifting it a bit.  This is complex, and HACKY in this code (happy to take PRs!) -
			// we need to do a curve fit on correlations[] around best_offset in order to better determine precise
			// (anti-aliased) offset.

			// we know best_offset >=1,
			// since foundGoodCorrelation cannot go to true until the second pass (offset=1), and
			// we can't drop into this clause until the following pass (else if).
			var shift = (correlations[best_offset+1] - correlations[best_offset-1])/correlations[best_offset];
			return sampleRate/(best_offset+(8*shift));
		}
		lastCorrelation = correlation;
	}
	if (best_correlation > 0.01) {
		// console.log("f = " + sampleRate/best_offset + "Hz (rms: " + rms + " confidence: " + best_correlation + ")")
		return sampleRate/best_offset;
	}
	return -1;
//	var best_frequency = sampleRate/best_offset;
}

function updatePitch( time ) {
	var cycles = new Array;
	analyser.getFloatTimeDomainData( buf );
	var ac = autoCorrelate( buf, audioContext.sampleRate );
	// TODO: Paint confidence meter on canvasElem here.

	if (DEBUGCANVAS) {  // This draws the current waveform, useful for debugging
		waveCanvas.clearRect(0,0,512,256);
		waveCanvas.strokeStyle = "red";
		waveCanvas.beginPath();
		waveCanvas.moveTo(0,0);
		waveCanvas.lineTo(0,256);
		waveCanvas.moveTo(128,0);
		waveCanvas.lineTo(128,256);
		waveCanvas.moveTo(256,0);
		waveCanvas.lineTo(256,256);
		waveCanvas.moveTo(384,0);
		waveCanvas.lineTo(384,256);
		waveCanvas.moveTo(512,0);
		waveCanvas.lineTo(512,256);
		waveCanvas.stroke();
		waveCanvas.strokeStyle = "black";
		waveCanvas.beginPath();
		waveCanvas.moveTo(0,buf[0]);
		for (var i=1;i<512;i++) {
			waveCanvas.lineTo(i,128+(buf[i]*128));
		}
		waveCanvas.stroke();
	}

 	if (ac == -1) {
 		detectorElem.className = "vague";
	 	pitchElem.innerText = "--";
		noteElem.innerText = "-";
		detuneElem.className = "";
		detuneAmount.innerText = "--";
 	} else {
	 	detectorElem.className = "confident";
	 	pitch = ac;
	 	pitchElem.innerText = Math.round( pitch ) ;
	 	var note =  noteFromPitch( pitch );
		noteElem.innerHTML = noteStrings[note%12];
		var detune = centsOffFromPitch( pitch, note );
		if (detune == 0 ) {
			detuneElem.className = "";
			detuneAmount.innerHTML = "--";
		} else {
			if (detune < 0)
				detuneElem.className = "flat";
			else
				detuneElem.className = "sharp";
			detuneAmount.innerHTML = Math.abs( detune );
		}
	}

	if (!window.requestAnimationFrame)
		window.requestAnimationFrame = window.webkitRequestAnimationFrame;
	rafID = window.requestAnimationFrame( updatePitch );
}
<|endoftext|>


<commit_before>var HTML_base = "http://projectchrono.org/metrics/api";
google.charts.load("current", {
    "packages": ["corechart"]
});
var charts = [];
var color_list = ['#274EC0', '#D12411', '#1E8E1B'];
// Shows dropdown menu of all tests available
function showTestNames(test_list) {
    for (i = 0; i < test_list.length; i++) {
        test = test_list[i];
        // Makes a dropdown list of each available test
        var x = document.getElementById("test_names");
        var option = document.createElement("option");
        option.text = test.name;
        option.value = test.name;
        x.add(option);
    }
}
// Parses data for a given test and plots charts for each metric
function drawCharts(test_runs) {
    var run_names = []; // Number of machines + name
    var runs = []; //  2D Array containing. Cols =  Number of machines, Rows = Run
    var metrics = [];
    run_names = test_runs["run_names"];
    var len = run_names.length;
    /* Load runs for each machine */
    for (var i = 0; i < len; i++) {
        runs.push(test_runs[run_names[i]]);
    }
    /* Load metric names */
    for (metric in runs[0][0]["metrics"]) {
        metrics.push(metric);
    }
    console.log(run_names, runs, metrics);
    showlegend(run_names);
    plotProps(metrics, run_names, runs);
}

function showlegend(run_names) {
    var table = document.createElement("table");
    for (var n = 0; n < run_names.length; n++) {
        var tr = document.createElement("tr");
        var colorbox = document.createElement("td");
        colorbox.setAttribute("style", "background-color:" + color_list[n]);
        colorbox.innerHTML = "
";
        tr.appendChild(colorbox);
        var buildername = document.createElement("td");
        buildername.innerHTML = run_names[n];
        tr.appendChild(buildername);
        table.appendChild(tr);
    }
    legendtitle = document.createElement("div");
    legendtitle.innerHTML = "
 Legend
";
    document.getElementById("metrics").appendChild(legendtitle);
    document.getElementById("metrics").appendChild(table);
}

function makeChart(data, prop_name) {
    var options = {
        title: prop_name,
        legend: "none",
        // vAxis: {title: metric_name},
        // hAxis: {title: "timestamp"},
        explorer: {
            axis: "horizontal",
            actions: ["dragToZoom", "rightClickToReset"],
            keepInBounds: true,
            maxZoomIn: .1
        },
        chartArea: {
            height: "50%",
            width: "90%"
        },
        colors: color_list
    };
    var div = document.createElement("div");
    div.setAttribute("id", prop_name);
    div.setAttribute("class", "metric");
    document.getElementById("metrics").appendChild(div);
    var chart = new google.visualization.ScatterChart(document.getElementById(prop_name));
    chart.draw(data, options);
    charts.push(chart);
}
// Plots charts for each property (metrics and execution time) of a given test
function plotProps(metrics, run_names, runs) {
    var timestamps = [];
    var commit_ids = [];
    var base_table = [
        ["x"]
    ];
    console.log(base_table);
    // Sets up table of timestamps
    for (var n = 0; n < run_names.length; n++) {
        // Iterate through each run for that name
        base_table[0].push(run_names[n]);
        for (var m = 0; m < runs[n].length; m++) {
            var test_run = runs[n][m];
            var ts = new Date(test_run["timestamp"]);
            // Adds timestamp to list of timestamps iff not already in array
            var index = getObjectIndex(ts, timestamps);
            if (index == -1) {
                timestamps.push(ts);
                base_table.push([ts]);
                // table[timestamps.length][n + 1] = test_run["metrics"][metric_name];
            } //else {
            // table[index + 1][n + 1].push(test_run["metrics"][metric_name]);
            // }
        }
    }
    // Ensures each row has same length
    for (var n = 0; n < base_table.length; n++) {
        base_table[n].length = run_names.length + 2;
    }
    var table = base_table;
    // Plots execution times
    for (var n = 0; n < run_names.length; n++) {
        // Iterate through each run for that name
        for (var m = 0; m < runs[n].length; m++) {
            var test_run = runs[n][m];
            var ts = new Date(test_run["timestamp"]);
            var index = getObjectIndex(ts, timestamps);
            table[index + 1][n + 1] = test_run["commit_id"];
            table[index + 1][n + 2] = test_run["execution_time"];
        }
    }
    console.log(table);
    var data = google.visualization.arrayToDataTable(table);
    data.setColumnProperty(1, 'role', 'tooltip');
    makeChart(data, "Execution Times");
    // console.log(metrics);
    for (var i = 0; i < metrics.length; i++) {
        var metric = metrics[i];
        // console.log(i + ": " + metric);
        table = base_table;
        // Plots a chart for each metric
        for (var n = 0; n < run_names.length; n++) {
            // Iterate through each run for that name
            for (var m = 0; m < runs[n].length; m++) {
                var test_run = runs[n][m];
                // console.log(test_run['metrics'][metric]);
                var ts = new Date(test_run["timestamp"]);
                var index = getObjectIndex(ts, timestamps);
                table[index + 1][n + 1] = test_run["commit_id"];
                table[index + 1][n + 2] = test_run["metrics"][metric];
            }
        }
        // console.log(table);
        data = google.visualization.arrayToDataTable(table);
        data.setColumnProperty(1, 'role', 'tooltip');
        makeChart(data, metric);
    }
}
// Finds index of specified object in an array
function getObjectIndex(obj, arr) {
    for (var test_idx = 0; test_idx < arr.length; test_idx++) {
        if (arr[test_idx].valueOf() == obj.valueOf()) {
            return test_idx;
        }
    }
    return -1;
}
// Sets up authentication info
$.ajaxSetup({
    crossDomain: true,
    xhrFields: {
        withCredentials: true
    },
    headers: {
        "Access-Control-Allow-Credentials": true,
        "Authorization": "Basic " + btoa("User:Password")
    }
});
// Gets list of test names
$.ajax({
        url: HTML_base + "/tests",
        method: "GET",
        data: "{};",
        dataType: "json",
        success: function(response, status, xhr) {
            console.log(response);
            // Changes "Test Loading!" text
            $("#test_names option:selected").html(" --- Select A Test --- ");
            showTestNames(response);
        },
        error: function(xhr, status, error_code) {
            console.log("Error:" + status + ": " + error_code);
        }
    })
    // Shows a test when selected from the dropdown menu
function showTest(test_name) {
    $("#metrics").empty(); // Clears metrics div so new charts can be shown
    if (test_name == "default") {
        return; //If the "Select A Test" option is selected
    }
    $.ajaxSetup({
        crossDomain: true,
        xhrFields: {
            withCredentials: true
        },
        headers: {
            "Access-Control-Allow-Credentials": true,
            "Authorization": "Basic " + btoa("User:Password")
        }
    });
    // Gets all test data for a given test
    $.ajax({
        url: HTML_base + "/tests/" + test_name,
        method: "GET",
        data: "{};",
        dataType: "json",
        success: function(response, status, xhr) {
            console.log(response);
            google.charts.setOnLoadCallback(drawCharts(response));
        },
        error: function(xhr, status, error_code) {
            console.log("Error:" + status + ": " + error_code);
        }
    })
}<commit_msg>moved commit id within table<commit_after>var HTML_base = "http://projectchrono.org/metrics/api";
google.charts.load("current", {
    "packages": ["corechart"]
});
var charts = [];
var color_list = ['#274EC0', '#D12411', '#1E8E1B'];
// Shows dropdown menu of all tests available
function showTestNames(test_list) {
    for (i = 0; i < test_list.length; i++) {
        test = test_list[i];
        // Makes a dropdown list of each available test
        var x = document.getElementById("test_names");
        var option = document.createElement("option");
        option.text = test.name;
        option.value = test.name;
        x.add(option);
    }
}
// Parses data for a given test and plots charts for each metric
function drawCharts(test_runs) {
    var run_names = []; // Number of machines + name
    var runs = []; //  2D Array containing. Cols =  Number of machines, Rows = Run
    var metrics = [];
    run_names = test_runs["run_names"];
    var len = run_names.length;
    /* Load runs for each machine */
    for (var i = 0; i < len; i++) {
        runs.push(test_runs[run_names[i]]);
    }
    /* Load metric names */
    for (metric in runs[0][0]["metrics"]) {
        metrics.push(metric);
    }
    console.log(run_names, runs, metrics);
    showlegend(run_names);
    plotProps(metrics, run_names, runs);
}

function showlegend(run_names) {
    var table = document.createElement("table");
    for (var n = 0; n < run_names.length; n++) {
        var tr = document.createElement("tr");
        var colorbox = document.createElement("td");
        colorbox.setAttribute("style", "background-color:" + color_list[n]);
        colorbox.innerHTML = "
";
        tr.appendChild(colorbox);
        var buildername = document.createElement("td");
        buildername.innerHTML = run_names[n];
        tr.appendChild(buildername);
        table.appendChild(tr);
    }
    legendtitle = document.createElement("div");
    legendtitle.innerHTML = "
 Legend
";
    document.getElementById("metrics").appendChild(legendtitle);
    document.getElementById("metrics").appendChild(table);
}

function makeChart(data, prop_name) {
    var options = {
        title: prop_name,
        legend: "none",
        // vAxis: {title: metric_name},
        // hAxis: {title: "timestamp"},
        explorer: {
            axis: "horizontal",
            actions: ["dragToZoom", "rightClickToReset"],
            keepInBounds: true,
            maxZoomIn: .1
        },
        chartArea: {
            height: "50%",
            width: "90%"
        },
        colors: color_list
    };
    var div = document.createElement("div");
    div.setAttribute("id", prop_name);
    div.setAttribute("class", "metric");
    document.getElementById("metrics").appendChild(div);
    var chart = new google.visualization.ScatterChart(document.getElementById(prop_name));
    chart.draw(data, options);
    charts.push(chart);
}
// Plots charts for each property (metrics and execution time) of a given test
function plotProps(metrics, run_names, runs) {
    var timestamps = [];
    var commit_ids = [];
    var base_table = [
        ["x"]
    ];
    console.log(base_table);
    // Sets up table of timestamps
    for (var n = 0; n < run_names.length; n++) {
        // Iterate through each run for that name
        base_table[0].push(run_names[n]);
        for (var m = 0; m < runs[n].length; m++) {
            var test_run = runs[n][m];
            var ts = new Date(test_run["timestamp"]);
            // Adds timestamp to list of timestamps iff not already in array
            var index = getObjectIndex(ts, timestamps);
            if (index == -1) {
                timestamps.push(ts);
                base_table.push([ts]);
                // table[timestamps.length][n + 1] = test_run["metrics"][metric_name];
            } //else {
            // table[index + 1][n + 1].push(test_run["metrics"][metric_name]);
            // }
        }
    }
    // Ensures each row has same length
    for (var n = 0; n < base_table.length; n++) {
        base_table[n].length = run_names.length + 2;
    }
    var table = base_table;
    // Plots execution times
    for (var n = 0; n < run_names.length; n++) {
        // Iterate through each run for that name
        for (var m = 0; m < runs[n].length; m++) {
            var test_run = runs[n][m];
            var ts = new Date(test_run["timestamp"]);
            var index = getObjectIndex(ts, timestamps);
            table[index + 1][run_names.length + 1] = test_run["commit_id"];
            table[index + 1][n + 2] = test_run["execution_time"];
        }
    }
    console.log(table);
    var data = google.visualization.arrayToDataTable(table);
    data.setColumnProperty(run_names.length + 1, 'role', 'tooltip');
    makeChart(data, "Execution Times");
    // console.log(metrics);
    for (var i = 0; i < metrics.length; i++) {
        var metric = metrics[i];
        // console.log(i + ": " + metric);
        table = base_table;
        // Plots a chart for each metric
        for (var n = 0; n < run_names.length; n++) {
            // Iterate through each run for that name
            for (var m = 0; m < runs[n].length; m++) {
                var test_run = runs[n][m];
                // console.log(test_run['metrics'][metric]);
                var ts = new Date(test_run["timestamp"]);
                var index = getObjectIndex(ts, timestamps);
                table[index + 1][run_names.length + 1] = test_run["commit_id"];
                table[index + 1][n + 2] = test_run["metrics"][metric];
            }
        }
        // console.log(table);
        data = google.visualization.arrayToDataTable(table);
        data.setColumnProperty(1, 'role', 'tooltip');
        makeChart(data, metric);
    }
}
// Finds index of specified object in an array
function getObjectIndex(obj, arr) {
    for (var test_idx = 0; test_idx < arr.length; test_idx++) {
        if (arr[test_idx].valueOf() == obj.valueOf()) {
            return test_idx;
        }
    }
    return -1;
}
// Sets up authentication info
$.ajaxSetup({
    crossDomain: true,
    xhrFields: {
        withCredentials: true
    },
    headers: {
        "Access-Control-Allow-Credentials": true,
        "Authorization": "Basic " + btoa("User:Password")
    }
});
// Gets list of test names
$.ajax({
        url: HTML_base + "/tests",
        method: "GET",
        data: "{};",
        dataType: "json",
        success: function(response, status, xhr) {
            console.log(response);
            // Changes "Test Loading!" text
            $("#test_names option:selected").html(" --- Select A Test --- ");
            showTestNames(response);
        },
        error: function(xhr, status, error_code) {
            console.log("Error:" + status + ": " + error_code);
        }
    })
    // Shows a test when selected from the dropdown menu
function showTest(test_name) {
    $("#metrics").empty(); // Clears metrics div so new charts can be shown
    if (test_name == "default") {
        return; //If the "Select A Test" option is selected
    }
    $.ajaxSetup({
        crossDomain: true,
        xhrFields: {
            withCredentials: true
        },
        headers: {
            "Access-Control-Allow-Credentials": true,
            "Authorization": "Basic " + btoa("User:Password")
        }
    });
    // Gets all test data for a given test
    $.ajax({
        url: HTML_base + "/tests/" + test_name,
        method: "GET",
        data: "{};",
        dataType: "json",
        success: function(response, status, xhr) {
            console.log(response);
            google.charts.setOnLoadCallback(drawCharts(response));
        },
        error: function(xhr, status, error_code) {
            console.log("Error:" + status + ": " + error_code);
        }
    })
}<|endoftext|>


<commit_before>/*
 * quick-reply.js
 * https://github.com/savetheinternet/Tinyboard/blob/master/js/quick-reply.js
 *
 * Released under the MIT license
 * Copyright (c) 2013 Michael Save <savetheinternet@tinyboard.org>
 *
 * Usage:
 *   $config['additional_javascript'][] = 'js/jquery.min.js';
 *   $config['additional_javascript'][] = 'js/jquery-ui.custom.min.js'; // Optional; if you want the form to be draggable.
 *   $config['additional_javascript'][] = 'js/quick-reply.js';
 *
 */

var do_css = function() {
	$('#quick-reply-css').remove();

	// Find background of reply posts
	var dummy_reply = $('

').appendTo($('body'));
	var reply_background = dummy_reply.css('background');
	dummy_reply.remove();

	$('').appendTo($('head'));
};

var show_quick_reply = function(){
	if($('div.banner').length == 0)
		return;
	if($('#quick-reply').length != 0)
		return;

	do_css();

	var $postForm = $('form[name="post"]').clone();

	$postForm.clone();

	$dummyStuff = $('

').appendTo($postForm);

	$postForm.find('table tr').each(function() {
		var $th = $(this).children('th:first');
		var $td = $(this).children('td:first');
		if ($th.length && $td.length) {
			$td.attr('colspan', 2);

			if ($td.find('input[type="text"]').length) {
				// Replace  with input placeholders
				$td.find('input[type="text"]')
					.removeAttr('size')
					.attr('placeholder', $th.clone().children().remove().end().text());
			}

			// Move anti-spam nonsense and remove
			$th.contents().appendTo($dummyStuff);
			$th.remove();

			if ($td.find('input[name="password"]').length) {
				// Hide password field
				$(this).hide();
			}

			// Fix submit button
			if ($td.find('input[type="submit"]').length) {
				$td.removeAttr('colspan');
				$('	').append($td.find('input[type="submit"]')).insertAfter($td);
			}

			if ($td.find('input[type="file"]').length) {
				if ($td.find('input[name="file_url"]').length) {
					$file_url = $td.find('input[name="file_url"]');

					// Make a new row for it
					$newRow = $('

');

					$file_url.clone().attr('placeholder', _('Upload URL')).appendTo($newRow.find('td'));
					$file_url.parent().remove();

					$newRow.insertBefore(this);

					$td.find('label').remove();
					$td.contents().filter(function() {
						return this.nodeType == 3; // Node.TEXT_NODE
					}).remove();
					$td.find('input[name="file_url"]').removeAttr('id');
				}

				if ($(this).find('input[name="spoiler"]').length) {
					$td.removeAttr('colspan');
				}
			}

			// Remove mod controls, because it looks shit.
			if ($td.find('input[type="checkbox"]').length) {
				var tr = this;
				$td.find('input[type="checkbox"]').each(function() {
					if ($(this).attr('name') == 'spoiler') {
						$td.find('label').remove();
						$(this).attr('id', 'q-spoiler-image');
						$postForm.find('input[type="file"]').parent()
							.removeAttr('colspan')
							.after($('	').append(this, ' ', $('').text(_('Spoiler Image'))));
					} else {
						$(tr).remove();
					}
				});
			}
		}
	});

	$postForm.find('textarea[name="body"]').removeAttr('id').removeAttr('cols').attr('placeholder', _('Comment'));

	$postForm.find('br').remove();
	$postForm.find('table').prepend('
' + _('Quick Reply') + '
');

	$postForm.attr('id', 'quick-reply');

	$postForm.appendTo($('body'));

	// Synchronise body text with original post form
	$('#body').bind('change input propertychange', function() {
		$postForm.find('textarea[name="body"]').val($(this).val());
	});
	$postForm.find('textarea[name="body"]').bind('change input propertychange', function() {
		$('#body').val($(this).val());
	});
	// Synchronise other inputs
	$('form[name="post"]:first input[type="text"],select').bind('change input propertychange', function() {
		$postForm.find('[name="' + $(this).attr('name') + '"]').val($(this).val());
	});
	$postForm.find('input[type="text"],select').bind('change input propertychange', function() {
		$('form[name="post"]:first [name="' + $(this).attr('name') + '"]').val($(this).val());
	});

	if (typeof $postForm.draggable != undefined) {
		if (localStorage.quickReplyPosition) {
			var offset = JSON.parse(localStorage.quickReplyPosition);
			if (offset.right > $(window).width() - $postForm.width())
				offset.right = $(window).width() - $postForm.width();
			if (offset.top > $(window).height() - $postForm.height())
				offset.top = $(window).height() - $postForm.height();
			$postForm.css('right', offset.right).css('top', offset.top);
		}
		$postForm.draggable({
			handle: 'th',
			containment: 'window',
			distance: 10,
			opacity: 0.9,
			scroll: false,
			stop: function() {
				var offset = {
					top: $(this).offset().top - $(window).scrollTop(),
					right: $(window).width() - $(this).offset().left - $(this).width(),
				};
				localStorage.quickReplyPosition = JSON.stringify(offset);

				$postForm.css('right', offset.right).css('top', offset.top).css('left', 'auto');
			}
		});
		$postForm.find('th').css('cursor', 'move');
	}

	$origPostForm = $('form[name="post"]');

	$(window).scroll(function() {
		if ($(this).width() <= 800)
			return;
		if ($(this).scrollTop() < $origPostForm.offset().top + $origPostForm.height() - 100)
			$postForm.fadeOut(100);
		else
			$postForm.fadeIn(100);
	}).on('stylesheet', function() {
		do_css();
		if ($('link#stylesheet').attr('href')) {
			$('link#stylesheet')[0].onload = do_css;
		}
	});
};

$(window).on('cite', function(e, id, with_link) {
	if ($(this).width() <= 800)
		return;
	show_quick_reply();
	$('#quick-reply textarea').focus();
	if (with_link) {
		$(window).ready(function() {
			if ($('#' + id).length) {
				highlightReply(id);
				$(window).scrollTop($('#' + id).offset().top);
			}
		});
	}
});
<commit_msg>slightly nicer<commit_after>/*
 * quick-reply.js
 * https://github.com/savetheinternet/Tinyboard/blob/master/js/quick-reply.js
 *
 * Released under the MIT license
 * Copyright (c) 2013 Michael Save <savetheinternet@tinyboard.org>
 *
 * Usage:
 *   $config['additional_javascript'][] = 'js/jquery.min.js';
 *   $config['additional_javascript'][] = 'js/jquery-ui.custom.min.js'; // Optional; if you want the form to be draggable.
 *   $config['additional_javascript'][] = 'js/quick-reply.js';
 *
 */

var do_css = function() {
	$('#quick-reply-css').remove();

	// Find background of reply posts
	var dummy_reply = $('

').appendTo($('body'));
	var reply_background = dummy_reply.css('background');
	dummy_reply.remove();

	$('').appendTo($('head'));
};

var show_quick_reply = function(){
	if($('div.banner').length == 0)
		return;
	if($('#quick-reply').length != 0)
		return;

	do_css();

	var $postForm = $('form[name="post"]').clone();

	$postForm.clone();

	$dummyStuff = $('

').appendTo($postForm);

	$postForm.find('table tr').each(function() {
		var $th = $(this).children('th:first');
		var $td = $(this).children('td:first');
		if ($th.length && $td.length) {
			$td.attr('colspan', 2);

			if ($td.find('input[type="text"]').length) {
				// Replace  with input placeholders
				$td.find('input[type="text"]')
					.removeAttr('size')
					.attr('placeholder', $th.clone().children().remove().end().text());
			}

			// Move anti-spam nonsense and remove
			$th.contents().appendTo($dummyStuff);
			$th.remove();

			if ($td.find('input[name="password"]').length) {
				// Hide password field
				$(this).hide();
			}

			// Fix submit button
			if ($td.find('input[type="submit"]').length) {
				$td.removeAttr('colspan');
				$('	').append($td.find('input[type="submit"]')).insertAfter($td);
			}

			if ($td.find('input[type="file"]').length) {
				if ($td.find('input[name="file_url"]').length) {
					$file_url = $td.find('input[name="file_url"]');

					// Make a new row for it
					$newRow = $('

');

					$file_url.clone().attr('placeholder', _('Upload URL')).appendTo($newRow.find('td'));
					$file_url.parent().remove();

					$newRow.insertBefore(this);

					$td.find('label').remove();
					$td.contents().filter(function() {
						return this.nodeType == 3; // Node.TEXT_NODE
					}).remove();
					$td.find('input[name="file_url"]').removeAttr('id');
				}

				if ($(this).find('input[name="spoiler"]').length) {
					$td.removeAttr('colspan');
				}
			}

			// Remove mod controls, because it looks shit.
			if ($td.find('input[type="checkbox"]').length) {
				var tr = this;
				$td.find('input[type="checkbox"]').each(function() {
					if ($(this).attr('name') == 'spoiler') {
						$td.find('label').remove();
						$(this).attr('id', 'q-spoiler-image');
						$postForm.find('input[type="file"]').parent()
							.removeAttr('colspan')
							.after($('	').append(this, ' ', $('').text(_('Spoiler Image'))));
					} else {
						$(tr).remove();
					}
				});
			}
		}
	});

	$postForm.find('textarea[name="body"]').removeAttr('id').removeAttr('cols').attr('placeholder', _('Comment'));

	$postForm.find('br').remove();
	$postForm.find('table').prepend('
' + _('Quick Reply') + '
');

	$postForm.attr('id', 'quick-reply');

	$postForm.appendTo($('body')).hide();

	// Synchronise body text with original post form
	$('#body').bind('change input propertychange', function() {
		$postForm.find('textarea[name="body"]').val($(this).val());
	});
	$postForm.find('textarea[name="body"]').bind('change input propertychange', function() {
		$('#body').val($(this).val());
	});
	// Synchronise other inputs
	$('form[name="post"]:first input[type="text"],select').bind('change input propertychange', function() {
		$postForm.find('[name="' + $(this).attr('name') + '"]').val($(this).val());
	});
	$postForm.find('input[type="text"],select').bind('change input propertychange', function() {
		$('form[name="post"]:first [name="' + $(this).attr('name') + '"]').val($(this).val());
	});

	if (typeof $postForm.draggable != undefined) {
		if (localStorage.quickReplyPosition) {
			var offset = JSON.parse(localStorage.quickReplyPosition);
			if (offset.right > $(window).width() - $postForm.width())
				offset.right = $(window).width() - $postForm.width();
			if (offset.top > $(window).height() - $postForm.height())
				offset.top = $(window).height() - $postForm.height();
			$postForm.css('right', offset.right).css('top', offset.top);
		}
		$postForm.draggable({
			handle: 'th',
			containment: 'window',
			distance: 10,
			opacity: 0.9,
			scroll: false,
			stop: function() {
				var offset = {
					top: $(this).offset().top - $(window).scrollTop(),
					right: $(window).width() - $(this).offset().left - $(this).width(),
				};
				localStorage.quickReplyPosition = JSON.stringify(offset);

				$postForm.css('right', offset.right).css('top', offset.top).css('left', 'auto');
			}
		});
		$postForm.find('th').css('cursor', 'move');
	}

	$postForm.show();
	$origPostForm = $('form[name="post"]');

	$(window).ready(function() {
		$(window).scroll(function() {
			if ($(this).width() <= 800)
				return;
			if ($(this).scrollTop() < $origPostForm.offset().top + $origPostForm.height() - 100)
				$postForm.fadeOut(100);
			else
				$postForm.fadeIn(100);
		}).on('stylesheet', function() {
			do_css();
			if ($('link#stylesheet').attr('href')) {
				$('link#stylesheet')[0].onload = do_css;
			}
		});
	});
};

$(window).on('cite', function(e, id, with_link) {
	if ($(this).width() <= 800)
		return;
	show_quick_reply();
	$('#quick-reply textarea').focus();
	if (with_link) {
		$(window).ready(function() {
			if ($('#' + id).length) {
				highlightReply(id);
				$(window).scrollTop($('#' + id).offset().top);
			}
		});
	}
});
<|endoftext|>


<commit_before>/**
 * renderer.js v0.1.0
 * Copyright (c) 2016. Collin Haines.
 * Licensed under MIT (https://github.com/collinhaines/terminal-game/blob/master/LICENSE)
 */

'use strict';

/**
 * Constructor
 */
function Visuals () {
  this.utils    = '';
  this.renderer = '';
  this.terminal = '';
}

/**
 * Event Listeners
 *
 * Attaches a mouse over, mouse out, and click event listener to each
 * `$('.text span')` element.
 *
 * Mouse over will determine if the character is a part of a population
 * of important notice (e.g. word, surrounding characters) and add a class
 * that visually shows the nearby characters are grouped for a reason.
 *
 * Mouse out will remove the visual.
 *
 * Click will do the initial discovery that mouse over does and then will
 * determine what occurs afterwards. Resulting in either a replenish, a dud
 * removal, an attempt decrease, a loss, a win, or nothing at all.
 */
Visuals.prototype.attachEventListeners = function () {
  const self = this;

  $(document).on('keydown', function (event) {
    // First, check if it was enter, and if there's something highlighted.
    if (event.which === 13 && $('.text span.is-hover').length > 0) {
      self.processInput();

      return false;
    }

    // Otherwise, if it was not an arrow key, neglect it.
    if ([37, 38, 39, 40].indexOf(event.which) === -1) {
      return false;
    }

    // Either highlight the "origin" of the board, or process where to go.
    if ($('.text span.is-hover').length === 0) {
      self.highlightAdd($('#text-1 > div:first-child > span:first-child'));
    } else {
      const $exact = $('.text span.is-hover[data-exact=true]');
      const $row   = $exact.parent();
      const $text  =   $row.parent();

      // Detect if unable to move left.
      if (event.which === 37 && $exact.is(':first-child') && $text.is('#text-1')) {
        return false;
      }

      // Detect if unable to move up.
      if (event.which === 38 && $row.is(':first-child')) {
        return false;
      }

      // Detect if unable to move right.
      if (event.which === 39 && $exact.is(':last-child') && $text.is('#text-2')) {
        return false;
      }

      // Detect if unable to move down.
      if (event.which === 40 && $row.is(':last-child')) {
        return false;
      }

      // From here, it's assumed the arrow key can move.
      self.highlightRemove($exact);

      // Detect if need to move from `#text-1` to `#text-2`.
      if (event.which === 39 && $exact.is(':last-child') && $text.is('#text-1')) {
        self.highlightAdd($('#text-2 > div:eq(' + $row.data('row') + ') > span').first());

        return false;
      }

      // Detect if need to move from `#text-2` to `#text-1`.
      if (event.which === 37 && $exact.is(':first-child') && $text.is('#text-2')) {
        self.highlightAdd($('#text-1 > div:eq(' + $row.data('row') + ') > span').last());

        return false;
      }

      // Default moving.
      switch (event.which) {
        // The left arrow key.
        case 37:
          let $left = $exact.prev();

          // Skip over the word, rather than go through each letter.
          if ($exact.is('[data-word]') && $left.is('[data-word]')) {
            const $word = $('[data-word="' + $left.data('word') + '"]');

            $left = $word.first().prev();

            // However, if the word is on two lines, do not skip the word.
            if ($left.parent().data('row') !== $exact.parent().data('row')) {
              $left = $exact.prev();
            }

            // Also, if the word is at the beginning of `#text-2`, mimic column shift.
            if ($row.find('> span:first-child').is($word) && $text.is('#text-2')) {
              $left = $('#text-1 > div:eq(' + $row.data('row') + ') > span').last();
            }
          }

          self.highlightAdd($left);
          break;

        // The up arrow key.
        case 38:
          self.highlightAdd($row.prev().find('[data-column="' + $exact.data('column') + '"]'));
          break;

        // The right arrow key.
        case 39:
          let $right = $exact.next();

          // Skip over the word, rather than go through each letter.
          if ($exact.is('[data-word]') && $right.is('[data-word]')) {
            const $word = $('[data-word="' + $right.data('word') + '"]');

            $right = $word.last().next();

            // However, if the word is on two lines, do not skip the word.
            if ($right.parent().data('row') !== $exact.parent().data('row')) {
              $right = $exact.next();
            }

            // Also, if the word is at the end of `#text-1`, mimic column shift.
            if ($row.find('> span:last-child').is($word) && $text.is('#text-1')) {
              $right = $('#text-2 > div:eq(' + $row.data('row') + ') > span').first();
            }
          }

          self.highlightAdd($right);
          break;

        // The down arrow key.
        case 40:
          self.highlightAdd($row.next().find('[data-column="' + $exact.data('column') + '"]'));
          break;
      }
    }
  });

  $('.text span')
    .mouseover(function () {
      $('.text span.is-hover').removeClass('is-hover');

      self.highlightAdd($(this));
    })
    .mouseout(function () {
      self.highlightRemove($(this));
    })
    .click(function () {
      self.processInput();
    });
};

/**
 * Ending Determiner
 *
 * Determines where within a given text that an ending bracket is located.
 *
 * @param  {String} text -- The text to search.
 * @return {Integer}
 */
Visuals.prototype.endingBracketLocation = function (text) {
  const brackets = ['>', ']', '}', ')'];

  for (let i = 0; i < brackets.length; i++) {
    if (text.indexOf(brackets[i]) > -1) {
      return text.indexOf(brackets[i]);
    }
  }

  return -1;
};

/**
 * Determines if the highlighted element has any notable relatives nearby.
 *
 * @param  {Element} element -- The initial highlighted element.
 * @return {Element or NodeList}
 */
Visuals.prototype.getPopulation = function ($element) {
  if (this.isBeginningBracket($element) && $element.attr('data-skip') === undefined) {
    return this.locateEnding($element);
  } else if ($element.is('[data-word]')) {
    return $('span[data-word="' + $element.attr('data-word') + '"]');
  } else {
    return $element;
  }
};

/**
 * Highlight Addition
 *
 * Adds a visual highlight of the character(s) selected.
 *
 * @param {Element} $element -- The current element.
 */
Visuals.prototype.highlightAdd = function ($element) {
  // Add our exact location.
  $element.attr('data-exact', true);

  // Determine if we have any friends.
  const $population = this.getPopulation($element);

  // Add the hovering to whomever is applicable.
  $population.addClass('is-hover');

  // Alert the entry of new text.
  $('#entry').html($population.text());
};

/**
 * Highlight Remover
 *
 * Removes a highlight from the current character(s) selected.
 *
 * @param {Element} $element -- The current element.
 */
Visuals.prototype.highlightRemove = function ($element) {
  // Remove our exact location.
  $element.removeAttr('data-exact');

  // Remove any traces that we were here.
  $('.text span.is-hover').removeClass('is-hover');

  // Even... even the entry.
  $('#entry').html('');
};

/**
 * Beginning Bracket Determiner
 *
 * Determines if the element is a beginning bracket.
 *
 * @param  {Element} $element -- The element being scanned.
 * @return {Boolean}
 */
Visuals.prototype.isBeginningBracket = function ($element) {
  return ['<', '[', '{', '('].indexOf($element.text()) > -1;
};

/**
 * Ending Locater
 *
 * Locates the ending of a surrounding block character within a row.
 *
 * @param  {Element} $element -- The current element in suspicion.
 * @return {Element}
 */
Visuals.prototype.locateEnding = function ($element) {
  const $row  = $element.parent();
  const index = this.endingBracketLocation($row.text());

  // If the row does not have the ending, return itself.
  if (index === -1) {
    return $element;
  }

  // Otherwise, return all the grouped elements.
  return $row.find('> span').slice($element.index(), index + 1);
};

/**
 * Processor
 *
 * Processes the selected character(s) and determines what to do.
 */
Visuals.prototype.processInput = function () {
  const $exact      = $('[data-exact="true"]');
  const password    = this.terminal.getPassword();
  const $population = this.getPopulation($exact);

  if (this.isBeginningBracket($exact) && $population.length > 1) {
    let output = '';

    if ($population.is('[data-replenishes]')) {
      // Replenish attempts, internally.
      this.terminal.replenishAttempts();

      // Replenish attempts, visually.
      this.renderer.renderAttempts(this.terminal.getAttempts());

      // Alert the user on what just happened.
      output = 'Tries reset.';
    } else {
      let words    = this.terminal.getWords();
      let removing = '';

      while (true) {
        const number = this.utils.randomNumberWithinRange(0, words.length);
        // Pick a random word.
        removing = words[number];

        // Do not pick the password.
        if (removing !== password) {
          // Remove the word from being able to be picked again.
          words.splice(number, 1);

          break;
        }

        // Set the words back in the terminal class.
        this.terminal.setWords(words);
      }

      // Remove the word visually.
      $('span[data-word="' + removing + '"]')
        .text('.')
        .removeAttr('data-word');

      // Aler the user on what just happened.
      output = 'Dud removed.';
    }

    this.renderer.print($population.text());
    this.renderer.print(output);

    // Remove this from being selected again.
    $population
      .attr('data-skip', true)
      .removeAttr('data-replenishes')
      .removeClass('is-hover');
  }

  if ($population.is('[data-word]')) {
    if ($population.text() === password) {
      // TODO: Something better.
      this.renderer.print($population.text().toUpperCase());
      this.renderer.print('Entry granted.');

      $('.text span').off('click');
      $(document).off('keydown');
    } else {
      // Decrease attempts, internally.
      this.terminal.decreaseAttempt();

      // Decrease attempts, visually.
      $('#attempts > .attempt:last-child').remove();

      // TODO: Does the word go away and/or clickable again in the game?

      // Detect the likeness.
      let likeness = 0;
      for (let i = 0; i < $population.text().length; i++) {
        if ($population.text()[i] === password[i]) {
          likeness++;
        }
      }

      // Render the output.
      this.renderer.print($population.text().toUpperCase());
      this.renderer.print('Entry denied.');
      this.renderer.print('Likeness=' + likeness);

      // Player has failed to hack into the terminal.
      if (this.terminal.getAttempts() === 0) {
        // TODO: Something better.
        this.renderer.print('Locked out.');

        $('.text span').off('click');
        $(document).off('keydown');
      }
    }
  }
};

/**
 * Mutators
 */
Visuals.prototype.setRenderer = function (renderer) {
  this.renderer = renderer;
};

Visuals.prototype.setTerminal = function (terminal) {
  this.terminal = terminal;
};

Visuals.prototype.setUtils = function (utils) {
  this.utils = utils;
};
<commit_msg>Exact Remover Bug<commit_after>/**
 * renderer.js v0.1.0
 * Copyright (c) 2016. Collin Haines.
 * Licensed under MIT (https://github.com/collinhaines/terminal-game/blob/master/LICENSE)
 */

'use strict';

/**
 * Constructor
 */
function Visuals () {
  this.utils    = '';
  this.renderer = '';
  this.terminal = '';
}

/**
 * Event Listeners
 *
 * Attaches a mouse over, mouse out, and click event listener to each
 * `$('.text span')` element.
 *
 * Mouse over will determine if the character is a part of a population
 * of important notice (e.g. word, surrounding characters) and add a class
 * that visually shows the nearby characters are grouped for a reason.
 *
 * Mouse out will remove the visual.
 *
 * Click will do the initial discovery that mouse over does and then will
 * determine what occurs afterwards. Resulting in either a replenish, a dud
 * removal, an attempt decrease, a loss, a win, or nothing at all.
 */
Visuals.prototype.attachEventListeners = function () {
  const self = this;

  $(document).on('keydown', function (event) {
    // First, check if it was enter, and if there's something highlighted.
    if (event.which === 13 && $('.text span.is-hover').length > 0) {
      self.processInput();

      return false;
    }

    // Otherwise, if it was not an arrow key, neglect it.
    if ([37, 38, 39, 40].indexOf(event.which) === -1) {
      return false;
    }

    // Either highlight the "origin" of the board, or process where to go.
    if ($('.text span.is-hover').length === 0) {
      self.highlightAdd($('#text-1 > div:first-child > span:first-child'));
    } else {
      const $exact = $('.text span.is-hover[data-exact=true]');
      const $row   = $exact.parent();
      const $text  =   $row.parent();

      // Detect if unable to move left.
      if (event.which === 37 && $exact.is(':first-child') && $text.is('#text-1')) {
        return false;
      }

      // Detect if unable to move up.
      if (event.which === 38 && $row.is(':first-child')) {
        return false;
      }

      // Detect if unable to move right.
      if (event.which === 39 && $exact.is(':last-child') && $text.is('#text-2')) {
        return false;
      }

      // Detect if unable to move down.
      if (event.which === 40 && $row.is(':last-child')) {
        return false;
      }

      // From here, it's assumed the arrow key can move.
      self.highlightRemove($exact);

      // Detect if need to move from `#text-1` to `#text-2`.
      if (event.which === 39 && $exact.is(':last-child') && $text.is('#text-1')) {
        self.highlightAdd($('#text-2 > div:eq(' + $row.data('row') + ') > span').first());

        return false;
      }

      // Detect if need to move from `#text-2` to `#text-1`.
      if (event.which === 37 && $exact.is(':first-child') && $text.is('#text-2')) {
        self.highlightAdd($('#text-1 > div:eq(' + $row.data('row') + ') > span').last());

        return false;
      }

      // Default moving.
      switch (event.which) {
        // The left arrow key.
        case 37:
          let $left = $exact.prev();

          // Skip over the word, rather than go through each letter.
          if ($exact.is('[data-word]') && $left.is('[data-word]')) {
            const $word = $('[data-word="' + $left.data('word') + '"]');

            $left = $word.first().prev();

            // However, if the word is on two lines, do not skip the word.
            if ($left.parent().data('row') !== $exact.parent().data('row')) {
              $left = $exact.prev();
            }

            // Also, if the word is at the beginning of `#text-2`, mimic column shift.
            if ($row.find('> span:first-child').is($word) && $text.is('#text-2')) {
              $left = $('#text-1 > div:eq(' + $row.data('row') + ') > span').last();
            }
          }

          self.highlightAdd($left);
          break;

        // The up arrow key.
        case 38:
          self.highlightAdd($row.prev().find('[data-column="' + $exact.data('column') + '"]'));
          break;

        // The right arrow key.
        case 39:
          let $right = $exact.next();

          // Skip over the word, rather than go through each letter.
          if ($exact.is('[data-word]') && $right.is('[data-word]')) {
            const $word = $('[data-word="' + $right.data('word') + '"]');

            $right = $word.last().next();

            // However, if the word is on two lines, do not skip the word.
            if ($right.parent().data('row') !== $exact.parent().data('row')) {
              $right = $exact.next();
            }

            // Also, if the word is at the end of `#text-1`, mimic column shift.
            if ($row.find('> span:last-child').is($word) && $text.is('#text-1')) {
              $right = $('#text-2 > div:eq(' + $row.data('row') + ') > span').first();
            }
          }

          self.highlightAdd($right);
          break;

        // The down arrow key.
        case 40:
          self.highlightAdd($row.next().find('[data-column="' + $exact.data('column') + '"]'));
          break;
      }
    }
  });

  $('.text span')
    .mouseover(function () {
      $('.text span.is-hover').removeClass('is-hover');

      self.highlightAdd($(this));
    })
    .mouseout(function () {
      self.highlightRemove($(this));
    })
    .click(function () {
      self.processInput();
    });
};

/**
 * Ending Determiner
 *
 * Determines where within a given text that an ending bracket is located.
 *
 * @param  {String} text -- The text to search.
 * @return {Integer}
 */
Visuals.prototype.endingBracketLocation = function (text) {
  const brackets = ['>', ']', '}', ')'];

  for (let i = 0; i < brackets.length; i++) {
    if (text.indexOf(brackets[i]) > -1) {
      return text.indexOf(brackets[i]);
    }
  }

  return -1;
};

/**
 * Determines if the highlighted element has any notable relatives nearby.
 *
 * @param  {Element} element -- The initial highlighted element.
 * @return {Element or NodeList}
 */
Visuals.prototype.getPopulation = function ($element) {
  if (this.isBeginningBracket($element) && $element.attr('data-skip') === undefined) {
    return this.locateEnding($element);
  } else if ($element.is('[data-word]')) {
    return $('span[data-word="' + $element.attr('data-word') + '"]');
  } else {
    return $element;
  }
};

/**
 * Highlight Addition
 *
 * Adds a visual highlight of the character(s) selected.
 *
 * @param {Element} $element -- The current element.
 */
Visuals.prototype.highlightAdd = function ($element) {
  // Add our exact location.
  $element.attr('data-exact', true);

  // Determine if we have any friends.
  const $population = this.getPopulation($element);

  // Add the hovering to whomever is applicable.
  $population.addClass('is-hover');

  // Alert the entry of new text.
  $('#entry').html($population.text());
};

/**
 * Highlight Remover
 *
 * Removes a highlight from the current character(s) selected.
 *
 * @param {Element} $element -- The current element.
 */
Visuals.prototype.highlightRemove = function ($element) {
  // Remove our exact location.
  $element.removeAttr('data-exact');

  // Remove any traces that we were here.
  $('.text span.is-hover').removeClass('is-hover');

  // Even... even the entry.
  $('#entry').html('');
};

/**
 * Beginning Bracket Determiner
 *
 * Determines if the element is a beginning bracket.
 *
 * @param  {Element} $element -- The element being scanned.
 * @return {Boolean}
 */
Visuals.prototype.isBeginningBracket = function ($element) {
  return ['<', '[', '{', '('].indexOf($element.text()) > -1;
};

/**
 * Ending Locater
 *
 * Locates the ending of a surrounding block character within a row.
 *
 * @param  {Element} $element -- The current element in suspicion.
 * @return {Element}
 */
Visuals.prototype.locateEnding = function ($element) {
  const $row  = $element.parent();
  const index = this.endingBracketLocation($row.text());

  // If the row does not have the ending, return itself.
  if (index === -1) {
    return $element;
  }

  // Otherwise, return all the grouped elements.
  return $row.find('> span').slice($element.index(), index + 1);
};

/**
 * Processor
 *
 * Processes the selected character(s) and determines what to do.
 */
Visuals.prototype.processInput = function () {
  const $exact      = $('[data-exact="true"]');
  const password    = this.terminal.getPassword();
  const $population = this.getPopulation($exact);

  if (this.isBeginningBracket($exact) && $population.length > 1) {
    let output = '';

    if ($population.is('[data-replenishes]')) {
      // Replenish attempts, internally.
      this.terminal.replenishAttempts();

      // Replenish attempts, visually.
      this.renderer.renderAttempts(this.terminal.getAttempts());

      // Alert the user on what just happened.
      output = 'Tries reset.';
    } else {
      let words    = this.terminal.getWords();
      let removing = '';

      while (true) {
        const number = this.utils.randomNumberWithinRange(0, words.length);
        // Pick a random word.
        removing = words[number];

        // Do not pick the password.
        if (removing !== password) {
          // Remove the word from being able to be picked again.
          words.splice(number, 1);

          break;
        }

        // Set the words back in the terminal class.
        this.terminal.setWords(words);
      }

      // Remove the word visually.
      $('span[data-word="' + removing + '"]')
        .text('.')
        .removeAttr('data-word');

      // Aler the user on what just happened.
      output = 'Dud removed.';
    }

    this.renderer.print($population.text());
    this.renderer.print(output);

    // Remove just the multiple items of hover.
    $population.removeClass('is-hover');

    // Remove this from being selected again.
    $exact
      .addClass('is-hover')
      .attr('data-skip', true)
      .removeAttr('data-replenishes');
  }

  if ($population.is('[data-word]')) {
    if ($population.text() === password) {
      // TODO: Something better.
      this.renderer.print($population.text().toUpperCase());
      this.renderer.print('Entry granted.');

      $('.text span').off('click');
      $(document).off('keydown');
    } else {
      // Decrease attempts, internally.
      this.terminal.decreaseAttempt();

      // Decrease attempts, visually.
      $('#attempts > .attempt:last-child').remove();

      // TODO: Does the word go away and/or clickable again in the game?

      // Detect the likeness.
      let likeness = 0;
      for (let i = 0; i < $population.text().length; i++) {
        if ($population.text()[i] === password[i]) {
          likeness++;
        }
      }

      // Render the output.
      this.renderer.print($population.text().toUpperCase());
      this.renderer.print('Entry denied.');
      this.renderer.print('Likeness=' + likeness);

      // Player has failed to hack into the terminal.
      if (this.terminal.getAttempts() === 0) {
        // TODO: Something better.
        this.renderer.print('Locked out.');

        $('.text span').off('click');
        $(document).off('keydown');
      }
    }
  }
};

/**
 * Mutators
 */
Visuals.prototype.setRenderer = function (renderer) {
  this.renderer = renderer;
};

Visuals.prototype.setTerminal = function (terminal) {
  this.terminal = terminal;
};

Visuals.prototype.setUtils = function (utils) {
  this.utils = utils;
};
<|endoftext|>


<commit_before>
drawStackedChart(10);

$(document).on('click', '#top_num li a', function () {
    if ($.trim($('#start-group').text()) == "Start year"){
      var start_year = 1955;
    } else {
      var start_year = parseFloat(
        $('#start-group').text().substr($('#start-group').text().length - 4));
    }
    if ($.trim($('#end-group').text()) == "End year"){
      var end_year = 2010;
    } else {
      var end_year = parseFloat(
        $('#end-group').text().substr($('#end-group').text().length - 4));
    }
    updateStackedChart(($(this).text()), start_year, end_year);
});

/*
$(document).on('click', '#start_year li a', function () {
    if ($.trim($('#words-group').text()) == "# of top words"){
      var terms_num = 10;
    } else {
      var terms_num = parseFloat(
        $('#words-group').text().substr($('#words-group').text().length - 2));
    }
    if ($.trim($('#end-group').text()) == "End year"){
      var end_year = 2010;
    } else {
      var end_year = parseFloat(
        $('#end-group').text().substr($('#end-group').text().length - 4));
    }
    var start_year = parseFloat(
        $('#start-group').text().substr($('#start-group').text().length - 4));
    updateStackedChart(terms_num, start_year, end_year);
});
$(document).on('click', '#end_year li a', function () {
    if ($.trim($('#words-group').text()) == "# of top words"){
      var terms_num = 10;
    } else {
      var terms_num = parseFloat(
        $('#words-group').text().substr($('#words-group').text().length - 2));
    }
    if ($.trim($('#start-group').text()) == "Start year"){
      var start_year = 1955;
    } else {
      var start_year = parseFloat(
        $('#start-group').text().substr($('#start-group').text().length - 4));
    }
    var end_year = parseFloat(
        $('#end-group').text().substr($('#end-group').text().length - 4));
    updateStackedChart(terms_num, start_year, end_year);
});
*/

$(function(){
    $("#top_num li a").click(function(){
      $("#words-group:first-child").text("# of top words: " + $(this).text());
      $("#words-group:first-child").val($(this).text());
   });
});
$(function(){
    $("#start_year li a").click(function(){
      $("#start-group:first-child").text("Start year: " + $(this).text());
      $("#start-group:first-child").val($(this).text());
   });
});
$(function(){
    $("#end_year li a").click(function(){
      $("#end-group:first-child").text("End year: " + $(this).text());
      $("#end-group:first-child").val($(this).text());
   });
});

var w = window,
    d = document,
    e = d.documentElement,
    g = d.getElementById('chart'),
    width = g.clientWidth/1.1, //w.innerWidth || e.clientWidth || g.clientWidth,
    height = e.clientHeight/1.3;//w.innerHeight|| e.clientHeight|| g.clientHeight;

var margin = {top: 20, right: 20, bottom: 30, left: 50},
    width = width - margin.left - margin.right,
    height = height - margin.top - margin.bottom;
/*
var margin = {top: 20, right: 20, bottom: 30, left: 50},
    width = 860 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;
*/

var tooltip = d3.select("div#chart")
    .append("div")
    .attr("class", "remove")
    .style("position", "absolute")
    .style("z-index", "20")
    .style("visibility", "hidden")
    .style("top", "500px")
    .style("left", "500px")
    .style("font-size", "16px");

var datearray = [];

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

var y = d3.scale.linear()
    .range([height, 0]);

var color = d3.scale.category20();

var xAxis = d3.svg.axis()
    .scale(x)
    .tickFormat(d3.format('d'))
    .orient("bottom");

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left");

var stack = d3.layout.stack()
  .offset('zero')
  .values(function(d){ return d.values; })
  .x(function(d){ return d.yearCol; })
  .y(function(d){ return d.countCol; });

var area = d3.svg.area()
    .interpolate('basis')
    .x(function(d) { return x(d.yearCol); })
    .y0(function(d) { return y(d.y0); })
    .y1(function(d) { return y(d.y0 + d.y); });

var svg = d3.select("div#chart").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 + ")");

function drawStackedChart(word_num){

  d3.csv('Words_allyears_27oct.csv', function(error, data){

    data.forEach(function(d){
      d.countCol = +d.countCol;
      d.yearCol = +d.yearCol;
    });

    var summed_data = d3.nest()
                        .key(function(d){ return d.wordCol; })
                        .rollup(function(leaves){
                            return {"total": d3.sum(
                              leaves, function(d){ return d.countCol; })}})
                        .entries(data);
    summed_data.sort(function(a,b){ return b.values.total - a.values.total; });

    var list = [];
    for (i=0; i<word_num; i++){
      list.push(summed_data[i].key);
    }

    var data = $.map(data, function(element){
      return ($.inArray(element.wordCol,list)>-1?element:null)
    });

    complete(data, list, data);

    data.sort(function(a,b){ return a.yearCol - b.yearCol; });

    var nest = d3.nest()
      .key(function(d){ return d.wordCol; })
      .entries(data);

    var layers = stack(nest);

    x.domain(d3.extent(data, function(d){ return d.yearCol; }));
    y.domain([0, d3.max(data, function(d){ return d.y0 + d.y; })]);

    var chartLayers = svg.selectAll(".layer")
        .data(layers);

    chartLayers
      .enter().append("path")
        .attr("class", "layer")
        .attr("d", function(d) { return area(d.values); })
        .style("fill", function(d, i) { return color(i); });

    chartLayers
        .transition()
        .duration(550)
        .attr("class", "layer")
        .attr("d", function(d) { return area(d.values); })
        .style("fill", function(d, i) { return color(i); });

    chartLayers
        .exit()
        .transition()
        .duration(550)
        .remove();

    svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis);

    svg.append("g")
        .attr("class", "y axis")
        .call(yAxis);

    svg.selectAll(".layer")
      .attr("opacity", 1)
      .on("mouseover", function(d, i) {
        svg.selectAll(".layer").transition()
        .duration(550)
        .attr("opacity", function(d, j) {
          return j != i ? 1 : 1;
      })})
      .on("mousemove", function(d, i) {
        mousex = d3.mouse(this);
        mousex = mousex[0];
        var invertedx = x.invert(mousex);
        //invertedx = invertedx.getMonth() + invertedx.getDate();
        var selected = (d.values);
        for (var k = 0; k < selected.length; k++) {
          datearray[k] = selected[k].yearCol;
        }

        mousedate = datearray.indexOf(Math.round(invertedx));
        pro = d.values[mousedate].countCol;
        tip_year = d.values[mousedate].yearCol;

        d3.select(this)
        .classed("hover", true)
        .attr("stroke", color[0])
        .attr("stroke-width", "0.5px"),
        tooltip.html( "
" + d.key + " | " + tip_year + ": " + pro + "
" ).style("visibility", "visible");

      })
      .on("mouseout", function(d, i) {
       svg.selectAll(".layer")
        .transition()
        .duration(250)
        .attr("opacity", "1");
        d3.select(this)
        .classed("hover", false)
        .attr("stroke-width", "0px"), tooltip.html( "
" + d.key + "
" + pro + "
" ).style("visibility", "hidden");
    });

  });
}

function updateStackedChart(word_num, start, end){
  console.log(word_num);
  console.log(start);
  console.log(end);
  d3.csv('Words_allyears_27oct.csv', function(error, data){

    data.forEach(function(d){
      d.countCol = +d.countCol;
      d.yearCol = +d.yearCol;
    });

    var summed_data = d3.nest()
                        .key(function(d){ return d.wordCol; })
                        .rollup(function(leaves){
                            return {"total": d3.sum(
                              leaves, function(d){ return d.countCol; })}})
                        .entries(data);
    summed_data.sort(function(a,b){ return b.values.total - a.values.total; });

    var list = [];
    for (i=0; i<word_num; i++){
      list.push(summed_data[i].key);
    }

    var data = $.map(data, function(element){
      return ($.inArray(element.wordCol,list)>-1?element:null)
    });

    complete(data, list, data);

    var year_list = [];
    for (i=start; i<end+1; i++){
      year_list.push(i);
    };

    data = $.map(data, function(element){
      return ($.inArray(element.yearCol, year_list) > -1 ? element:null)
    });

    data.sort(function(a,b){ return a.yearCol - b.yearCol; });

    var nest = d3.nest()
      .key(function(d){ return d.wordCol; })
      .entries(data);

    var layers = stack(nest);

    x.domain(d3.extent(data, function(d){ return d.yearCol; }));
    y.domain([0, d3.max(data, function(d){ return d.y0 + d.y; })]);

    var chartLayers = svg.selectAll(".layer")
        .data(layers);

    chartLayers
      .enter().append("path")
        .attr("class", "layer")
        .attr("d", function(d) { return area(d.values); })
        .style("fill", function(d, i) { return color(i); });

    chartLayers
        .transition()
        .duration(550)
        .attr("class", "layer")
        .attr("d", function(d) { return area(d.values); })
        .style("fill", function(d, i) { return color(i); });

    chartLayers
        .exit()
        .transition()
        .duration(550)
        .remove();

    svg.selectAll('.x.axis')
        .transition()
        .duration(550)
        .call(xAxis);

    svg.selectAll('.y.axis')
        .transition()
        .duration(550)
        .call(yAxis);

    svg.selectAll(".layer")
      .attr("opacity", 1)
      /*
      .on("mouseover", function(d, i) {
        svg.selectAll(".layer").transition()
        .duration(250)
        .attr("opacity", function(d, j) {
          return j != i ? 0.6 : 1;
      })})
      */
      .on("mousemove", function(d, i) {
        mousex = d3.mouse(this);
        mousex = mousex[0];
        var invertedx = x.invert(mousex);
        //invertedx = invertedx.getMonth() + invertedx.getDate();
        var selected = (d.values);
        for (var k = 0; k < selected.length; k++) {
          datearray[k] = selected[k].yearCol;
        }

        mousedate = datearray.indexOf(Math.round(invertedx));
        pro = d.values[mousedate].countCol;
        tip_year = d.values[mousedate].yearCol;

        d3.select(this)
        .classed("hover", true)
        .attr("stroke", color[0])
        .attr("stroke-width", "0.5px"),
        tooltip.html( "
" + d.key + " | " + tip_year + ": " + pro + "
" ).style("visibility", "visible");

      })
      .on("mouseout", function(d, i) {
       svg.selectAll(".layer")
        .transition()
        .duration(550)
        .attr("opacity", "1");
        d3.select(this)
        .classed("hover", false)
        .attr("stroke-width", "0px"), tooltip.html( "
" + d.key + "
" + pro + "
" ).style("visibility", "hidden");
    });
  });
}

function complete(x, key, data){
  for (i=0; i<key.length; i++){
    tempdata = x.filter(function(d){ return d.wordCol == key[i]; });
    yearsPresent = []
    years = [];

    for (year=1955; year<2010; year++){
      years.push(year)
      for (row=0; row<tempdata.length; row++){
        if (tempdata[row].yearCol === year){
          yearsPresent.push(year);
        }
      }
    }

    for (year=0; year<years.length; year++){
      if (yearsPresent.indexOf(years[year]) < 0){
        dict = {}
        dict['countCol'] = 0;
        dict['wordCol'] = tempdata[0].wordCol;
        dict['yearCol'] = years[year];
        data.push(dict);
      }
    }
  }

};
  <commit_msg>resize window<commit_after>
drawStackedChart(10);

$(document).on('click', '#top_num li a', function () {
    if ($.trim($('#start-group').text()) == "Start year"){
      var start_year = 1955;
    } else {
      var start_year = parseFloat(
        $('#start-group').text().substr($('#start-group').text().length - 4));
    }
    if ($.trim($('#end-group').text()) == "End year"){
      var end_year = 2010;
    } else {
      var end_year = parseFloat(
        $('#end-group').text().substr($('#end-group').text().length - 4));
    }
    updateStackedChart(($(this).text()), start_year, end_year);
});

/*
$(document).on('click', '#start_year li a', function () {
    if ($.trim($('#words-group').text()) == "# of top words"){
      var terms_num = 10;
    } else {
      var terms_num = parseFloat(
        $('#words-group').text().substr($('#words-group').text().length - 2));
    }
    if ($.trim($('#end-group').text()) == "End year"){
      var end_year = 2010;
    } else {
      var end_year = parseFloat(
        $('#end-group').text().substr($('#end-group').text().length - 4));
    }
    var start_year = parseFloat(
        $('#start-group').text().substr($('#start-group').text().length - 4));
    updateStackedChart(terms_num, start_year, end_year);
});
$(document).on('click', '#end_year li a', function () {
    if ($.trim($('#words-group').text()) == "# of top words"){
      var terms_num = 10;
    } else {
      var terms_num = parseFloat(
        $('#words-group').text().substr($('#words-group').text().length - 2));
    }
    if ($.trim($('#start-group').text()) == "Start year"){
      var start_year = 1955;
    } else {
      var start_year = parseFloat(
        $('#start-group').text().substr($('#start-group').text().length - 4));
    }
    var end_year = parseFloat(
        $('#end-group').text().substr($('#end-group').text().length - 4));
    updateStackedChart(terms_num, start_year, end_year);
});
*/

$(function(){
    $("#top_num li a").click(function(){
      $("#words-group:first-child").text("# of top words: " + $(this).text());
      $("#words-group:first-child").val($(this).text());
   });
});
$(function(){
    $("#start_year li a").click(function(){
      $("#start-group:first-child").text("Start year: " + $(this).text());
      $("#start-group:first-child").val($(this).text());
   });
});
$(function(){
    $("#end_year li a").click(function(){
      $("#end-group:first-child").text("End year: " + $(this).text());
      $("#end-group:first-child").val($(this).text());
   });
});

var w = window,
    d = document,
    e = d.documentElement,
    g = d.getElementById('chart'),
    width = g.clientWidth/1.1, //w.innerWidth || e.clientWidth || g.clientWidth,
    height = e.clientHeight/1.45;//w.innerHeight|| e.clientHeight|| g.clientHeight;

var margin = {top: 20, right: 20, bottom: 30, left: 50},
    width = width - margin.left - margin.right,
    height = height - margin.top - margin.bottom;
/*
var margin = {top: 20, right: 20, bottom: 30, left: 50},
    width = 860 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;
*/

var tooltip = d3.select("div#chart")
    .append("div")
    .attr("class", "remove")
    .style("position", "absolute")
    .style("z-index", "20")
    .style("visibility", "hidden")
    .style("top", "500px")
    .style("left", "500px")
    .style("font-size", "16px");

var datearray = [];

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

var y = d3.scale.linear()
    .range([height, 0]);

var color = d3.scale.category20();

var xAxis = d3.svg.axis()
    .scale(x)
    .tickFormat(d3.format('d'))
    .orient("bottom");

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left");

var stack = d3.layout.stack()
  .offset('zero')
  .values(function(d){ return d.values; })
  .x(function(d){ return d.yearCol; })
  .y(function(d){ return d.countCol; });

var area = d3.svg.area()
    .interpolate('basis')
    .x(function(d) { return x(d.yearCol); })
    .y0(function(d) { return y(d.y0); })
    .y1(function(d) { return y(d.y0 + d.y); });

var svg = d3.select("div#chart").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 + ")");

function drawStackedChart(word_num){

  d3.csv('Words_allyears_27oct.csv', function(error, data){

    data.forEach(function(d){
      d.countCol = +d.countCol;
      d.yearCol = +d.yearCol;
    });

    var summed_data = d3.nest()
                        .key(function(d){ return d.wordCol; })
                        .rollup(function(leaves){
                            return {"total": d3.sum(
                              leaves, function(d){ return d.countCol; })}})
                        .entries(data);
    summed_data.sort(function(a,b){ return b.values.total - a.values.total; });

    var list = [];
    for (i=0; i<word_num; i++){
      list.push(summed_data[i].key);
    }

    var data = $.map(data, function(element){
      return ($.inArray(element.wordCol,list)>-1?element:null)
    });

    complete(data, list, data);

    data.sort(function(a,b){ return a.yearCol - b.yearCol; });

    var nest = d3.nest()
      .key(function(d){ return d.wordCol; })
      .entries(data);

    var layers = stack(nest);

    x.domain(d3.extent(data, function(d){ return d.yearCol; }));
    y.domain([0, d3.max(data, function(d){ return d.y0 + d.y; })]);

    var chartLayers = svg.selectAll(".layer")
        .data(layers);

    chartLayers
      .enter().append("path")
        .attr("class", "layer")
        .attr("d", function(d) { return area(d.values); })
        .style("fill", function(d, i) { return color(i); });

    chartLayers
        .transition()
        .duration(550)
        .attr("class", "layer")
        .attr("d", function(d) { return area(d.values); })
        .style("fill", function(d, i) { return color(i); });

    chartLayers
        .exit()
        .transition()
        .duration(550)
        .remove();

    svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis);

    svg.append("g")
        .attr("class", "y axis")
        .call(yAxis);

    svg.selectAll(".layer")
      .attr("opacity", 1)
      .on("mouseover", function(d, i) {
        svg.selectAll(".layer").transition()
        .duration(550)
        .attr("opacity", function(d, j) {
          return j != i ? 1 : 1;
      })})
      .on("mousemove", function(d, i) {
        mousex = d3.mouse(this);
        mousex = mousex[0];
        var invertedx = x.invert(mousex);
        //invertedx = invertedx.getMonth() + invertedx.getDate();
        var selected = (d.values);
        for (var k = 0; k < selected.length; k++) {
          datearray[k] = selected[k].yearCol;
        }

        mousedate = datearray.indexOf(Math.round(invertedx));
        pro = d.values[mousedate].countCol;
        tip_year = d.values[mousedate].yearCol;

        d3.select(this)
        .classed("hover", true)
        .attr("stroke", color[0])
        .attr("stroke-width", "0.5px"),
        tooltip.html( "
" + d.key + " | " + tip_year + ": " + pro + "
" ).style("visibility", "visible");

      })
      .on("mouseout", function(d, i) {
       svg.selectAll(".layer")
        .transition()
        .duration(250)
        .attr("opacity", "1");
        d3.select(this)
        .classed("hover", false)
        .attr("stroke-width", "0px"), tooltip.html( "
" + d.key + "
" + pro + "
" ).style("visibility", "hidden");
    });

  });
}

function updateStackedChart(word_num, start, end){
  console.log(word_num);
  console.log(start);
  console.log(end);
  d3.csv('Words_allyears_27oct.csv', function(error, data){

    data.forEach(function(d){
      d.countCol = +d.countCol;
      d.yearCol = +d.yearCol;
    });

    var summed_data = d3.nest()
                        .key(function(d){ return d.wordCol; })
                        .rollup(function(leaves){
                            return {"total": d3.sum(
                              leaves, function(d){ return d.countCol; })}})
                        .entries(data);
    summed_data.sort(function(a,b){ return b.values.total - a.values.total; });

    var list = [];
    for (i=0; i<word_num; i++){
      list.push(summed_data[i].key);
    }

    var data = $.map(data, function(element){
      return ($.inArray(element.wordCol,list)>-1?element:null)
    });

    complete(data, list, data);

    var year_list = [];
    for (i=start; i<end+1; i++){
      year_list.push(i);
    };

    data = $.map(data, function(element){
      return ($.inArray(element.yearCol, year_list) > -1 ? element:null)
    });

    data.sort(function(a,b){ return a.yearCol - b.yearCol; });

    var nest = d3.nest()
      .key(function(d){ return d.wordCol; })
      .entries(data);

    var layers = stack(nest);

    x.domain(d3.extent(data, function(d){ return d.yearCol; }));
    y.domain([0, d3.max(data, function(d){ return d.y0 + d.y; })]);

    var chartLayers = svg.selectAll(".layer")
        .data(layers);

    chartLayers
      .enter().append("path")
        .attr("class", "layer")
        .attr("d", function(d) { return area(d.values); })
        .style("fill", function(d, i) { return color(i); });

    chartLayers
        .transition()
        .duration(550)
        .attr("class", "layer")
        .attr("d", function(d) { return area(d.values); })
        .style("fill", function(d, i) { return color(i); });

    chartLayers
        .exit()
        .transition()
        .duration(550)
        .remove();

    svg.selectAll('.x.axis')
        .transition()
        .duration(550)
        .call(xAxis);

    svg.selectAll('.y.axis')
        .transition()
        .duration(550)
        .call(yAxis);

    svg.selectAll(".layer")
      .attr("opacity", 1)
      /*
      .on("mouseover", function(d, i) {
        svg.selectAll(".layer").transition()
        .duration(250)
        .attr("opacity", function(d, j) {
          return j != i ? 0.6 : 1;
      })})
      */
      .on("mousemove", function(d, i) {
        mousex = d3.mouse(this);
        mousex = mousex[0];
        var invertedx = x.invert(mousex);
        //invertedx = invertedx.getMonth() + invertedx.getDate();
        var selected = (d.values);
        for (var k = 0; k < selected.length; k++) {
          datearray[k] = selected[k].yearCol;
        }

        mousedate = datearray.indexOf(Math.round(invertedx));
        pro = d.values[mousedate].countCol;
        tip_year = d.values[mousedate].yearCol;

        d3.select(this)
        .classed("hover", true)
        .attr("stroke", color[0])
        .attr("stroke-width", "0.5px"),
        tooltip.html( "
" + d.key + " | " + tip_year + ": " + pro + "
" ).style("visibility", "visible");

      })
      .on("mouseout", function(d, i) {
       svg.selectAll(".layer")
        .transition()
        .duration(550)
        .attr("opacity", "1");
        d3.select(this)
        .classed("hover", false)
        .attr("stroke-width", "0px"), tooltip.html( "
" + d.key + "
" + pro + "
" ).style("visibility", "hidden");
    });
  });
}

function complete(x, key, data){
  for (i=0; i<key.length; i++){
    tempdata = x.filter(function(d){ return d.wordCol == key[i]; });
    yearsPresent = []
    years = [];

    for (year=1955; year<2010; year++){
      years.push(year)
      for (row=0; row<tempdata.length; row++){
        if (tempdata[row].yearCol === year){
          yearsPresent.push(year);
        }
      }
    }

    for (year=0; year<years.length; year++){
      if (yearsPresent.indexOf(years[year]) < 0){
        dict = {}
        dict['countCol'] = 0;
        dict['wordCol'] = tempdata[0].wordCol;
        dict['yearCol'] = years[year];
        data.push(dict);
      }
    }
  }

};
  <|endoftext|>