<commit_before>var nextID = 1;

window.onload = function() {
	var divs = document.getElementsByClassName("mfsviewer");
	for (var i = 0; i < divs.length; i++) {
		new MFSViewer(divs[i], divs[i].renderSettings ? divs[i].renderSettings : window.renderSettings);
	}
}

function MFSViewer(div, settings) {
	var myself = this;

	this.animate = function() {
		requestAnimationFrame(myself.animate);
		myself.controls.update();
		if (myself.frameCount < myself.frameCountTarget) {
			myself.render();
			myself.frameCount++;
			myself.log("Rendered " + myself.frameCount + "/" + myself.frameCountTarget + " frames");
		}
	};

	this.log = function(logContent) {
		console.log("[MFSViewer #"+myself.id+"]", logContent);
	}

	this.requestRender = function() {
		myself.log("Requesting new frame set!");
		myself.frameCount = 0;
	}

	this.render = function() {
		if (this.mainSceneShaderMaterial.uniforms.antiAliasing.value) {
			var xRand = Math.random() - 0.5;
			var yRand = Math.random() - 0.5;
			this.mainSceneShaderMaterial.uniforms.aaNdcOffset.value.x = 2 * xRand / (this.width);
			this.mainSceneShaderMaterial.uniforms.aaNdcOffset.value.y = 2 * yRand / (this.height);
		}

		// generate new frame from main scene
		this.renderer.render(this.mainScene, this.mainCamera, this.newFrameBuffer);
		this.mixSceneShaderMaterial.uniforms.newFrame.value = this.newFrameBuffer.texture;

		this.mixSceneShaderMaterial.uniforms.lastFrame.value = this.bufferFlipFlop ? this.secondAccumBuffer.texture : this.firstAccumBuffer.texture;
		this.mixSceneShaderMaterial.uniforms.weight.value = this.frameCount / (this.frameCount + 1);

		// mix our accum buffer with our new frame in the mix scene
		if(this.bufferFlipFlop)
			this.renderer.render(this.mixScene, this.mixCamera, this.firstAccumBuffer, false);
		else
			this.renderer.render(this.mixScene, this.mixCamera, this.secondAccumBuffer, false);

		// render the output from the mix scene to our final scene and
		this.finalQuad.material.map = !this.bufferFlipFlop ? this.secondAccumBuffer.texture : this.firstAccumBuffer.texture;
		this.renderer.render(this.finalScene, this.finalCamera);

		this.bufferFlipFlop = !this.bufferFlipFlop;
	}

	this.resize = function() {
		if (myself.fixedSize) {
				return;
		}
		var w = myself.div.offsetParent.offsetWidth, h = myself.div.offsetParent.offsetHeight;
		myself.renderer.setSize(w, h);
		w *= myself.dpr;
		h *= myself.dpr;
		myself.mainCamera.aspect = w / h;
		myself.mainCamera.updateProjectionMatrix();
		myself.firstAccumBuffer.setSize(w, h);
		myself.secondAccumBuffer.setSize(w, h);
		myself.newFrameBuffer.setSize(w, h);
		myself.mixSceneShaderMaterial.uniforms.viewport.value = new THREE.Vector2(w, h);
		myself.width = w;
		myself.height = h;
		myself.requestRender();
	}

	// set width & height
	this.fixedSize = !(settings.width == undefined || settings.height == undefined);
	this.width = settings.width ? settings.width : div.offsetParent.offsetWidth;
	this.height = settings.height ? settings.height : div.offsetParent.offsetHeight;
	window.addEventListener('resize', this.resize, false);
	this.dpr = window.devicePixelRatio;

	// misc vars
	this.frameCount = 0;
	this.frameCountTarget = 64;
	this.bufferFlipFlop = true;
	this.id = nextID++;

	// prepare renderer
	this.renderer = new THREE.WebGLRenderer( { alpha: true } );
	this.renderer.setSize(this.width, this.height);
	this.renderer.setPixelRatio(this.dpr);
	this.width = this.width * this.dpr;
	this.height = this.height * this.dpr;
	this.renderer.setClearColor(0x000000, 0);

	// set up buffers
	var texturePrecision;
	if (this.renderer.context.getExtension('OES_texture_float') !== null) {
		texturePrecision = THREE.FloatType;
		this.log('FLOAT texture precision supported and will be used.');
	} else if (this.renderer.context.getExtension('OES_texture_half_float') !== null) {
		texturePrecision = THREE.HalfFloatType;
		this.log('HALFFLOAT texture precision supported and will be used.');
	} else {
		texturePrecision = THREE.UnsignedByteType;
		this.log('UNSIGNED BYTE texture precision will be used.');
	}
	var bufferSettings = {
		minFilter: THREE.NearestFilter,
		magFilter: THREE.NearestFilter,
		format: THREE.RGBAFormat,
		type: texturePrecision
	};
	this.firstAccumBuffer = new THREE.WebGLRenderTarget(this.width, this.height, bufferSettings);
	this.secondAccumBuffer = new THREE.WebGLRenderTarget(this.width, this.height, bufferSettings);
	this.newFrameBuffer = new THREE.WebGLRenderTarget(this.width, this.height, bufferSettings);

	// initialize cameras
	this.mainCamera = new THREE.PerspectiveCamera(75, this.width / this.height, 0.1, 2000);
	this.mainCamera.position.z = 7;
	this.mixCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
	this.finalCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);

	// prepare scenes
	this.mainScene = new THREE.Scene();
	this.mainScene.add(this.mainCamera);
	this.mixScene = new THREE.Scene();
	this.mixScene.add(this.mixCamera);
	this.finalScene = new THREE.Scene();
	this.finalScene.add(this.finalCamera);

	// load shaders
	var mainSceneVertexShader = `
		// switch on high precision floats
		#ifdef GL_ES
		precision highp float;
		#endif

		varying vec3 vNormal;
		uniform bool antiAliasing;
		uniform vec2 aaNdcOffset;

		void main() {
				vNormal = normal;
				vec4 ndcVertex = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
				if (antiAliasing) {
					ndcVertex.xy += aaNdcOffset * ndcVertex.w;
				}
				gl_Position = ndcVertex;
			}
		`;
	var mainSceneFragmentShader = `
		// switch on high precision floats
		#ifdef GL_ES
		precision highp float;
		#endif

		varying vec3 vNormal;

		void main() {
				float dProd = max(0.0, dot(vNormal, normalize(cameraPosition)));
				gl_FragColor = vec4(dProd, dProd, dProd, 1.0);
			}
		`;
	var mixSceneVertexShader = `
		// switch on high precision floats
		#ifdef GL_ES
		precision highp float;
		#endif

		void main() {
				gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
			}
		`;
	var mixSceneFragmentShader = `
		// switch on high precision floats
		#ifdef GL_ES
		precision highp float;
		#endif

		uniform vec2 viewport;
		uniform float weight;
		uniform sampler2D newFrame;
		uniform sampler2D lastFrame;

		void main() {
				vec4 newColor = texture2D(newFrame, gl_FragCoord.xy / viewport.xy);
				vec4 accColor = texture2D(lastFrame, gl_FragCoord.xy / viewport.xy);
				gl_FragColor = mix(newColor, accColor, weight);
			}
		`;
	this.mainSceneShaderMaterial = new THREE.ShaderMaterial({
		uniforms: {
			antiAliasing: { value: settings.antiAliasing != undefined ? settings.antiAliasing : true },
			aaNdcOffset: { value: new THREE.Vector2(0.0, 0.0) }
		},
		vertexShader: mainSceneVertexShader,
		fragmentShader: mainSceneFragmentShader
	});
	this.mixSceneShaderMaterial = new THREE.ShaderMaterial({
		uniforms: {
			lastFrame: { value: this.firstAccumBuffer.texture },
			newFrame: { value: this.firstAccumBuffer.texture },
			weight: { value: 0.0 },
			viewport: { value: new THREE.Vector2(this.width, this.height) }
		},
		vertexShader: mixSceneVertexShader,
		fragmentShader: mixSceneFragmentShader
	});

	// load and add our object to the scene
	var manager = new THREE.LoadingManager();
	var log = this.log, animate = this.animate;
	manager.onProgress = function (item, loaded, total) {
		log("Loaded item " + item + " (" + loaded + " of " + total + " objects)");
	};
	manager.onLoad = function () {
		log("Loading finished!");
		animate();
	};

	var loader = new THREE.OBJLoader(manager);
	loader.load(settings.objPath, function (object) {
		object.traverse(function(child) {
			if (child instanceof THREE.Mesh) {
				child.material = myself.mainSceneShaderMaterial;
			}
		} );
		myself.mainScene.add(object);
	} );

	// load quad for finalScene
	this.mixQuad = new THREE.Mesh(new THREE.PlaneBufferGeometry(2, 2), this.mixSceneShaderMaterial);
	this.mixScene.add(this.mixQuad);
	this.finalQuad = new THREE.Mesh(new THREE.PlaneBufferGeometry(2, 2), new THREE.MeshBasicMaterial( { transparent: true } ));
	this.finalScene.add(this.finalQuad);

	// attach the render-supplied DOM element
	div.appendChild(this.renderer.domElement);
	this.div = div;

	// configure trackball controls
	this.controls = new THREE.TrackballControls(this.mainCamera, this.renderer.domElement);
	this.controls.addEventListener('change', this.requestRender);
	this.controls.target.set(0, 0, 0);
	this.controls.rotateSpeed = 5.0;
	this.controls.zoomSpeed = 1.2;
	this.controls.panSpeed = 0.8;
	this.controls.noZoom = false;
	this.controls.noPan = false;
	this.controls.staticMoving = true;
	this.controls.dynamicDampingFactor = 0.3;
	this.controls.keys = [ 65, 83, 68 ];

	// set up gui
	this.gui = new dat.GUI();
	this.gui.width = 300;
	var guiOptions = {
		"ViewerID(ReadOnly)": myself.id,
		targetFrameCount: "64",
		antiAliasing: this.mainSceneShaderMaterial.uniforms.antiAliasing.value
	};
	var updateTargetFrameCount = function() {
		var newFrameCountTarget = parseFloat(guiOptions.targetFrameCount).toFixed(0);
		if (newFrameCountTarget != myself.frameCountTarget && newFrameCountTarget > 0) {
			myself.frameCountTarget = newFrameCountTarget;
			myself.requestRender();
		}
	};
	var updateRenderSettings = function () {
		myself.mainSceneShaderMaterial.uniforms.antiAliasing.value = guiOptions.antiAliasing;
		myself.requestRender();
	}
	updateTargetFrameCount();
	updateRenderSettings();
	this.gui.add(guiOptions, "ViewerID(ReadOnly)");
	this.gui.add(guiOptions, "targetFrameCount").onChange(updateTargetFrameCount);
	this.gui.add(guiOptions, "antiAliasing").onChange(updateRenderSettings);
}
<commit_msg>Corrected [half]float texture support detection. Multi-line strings are now more compatible.<commit_after>var nextID = 1;

window.onload = function() {
	var divs = document.getElementsByClassName("mfsviewer");
	for (var i = 0; i < divs.length; i++) {
		new MFSViewer(divs[i], divs[i].renderSettings ? divs[i].renderSettings : window.renderSettings);
	}
}

function MFSViewer(div, settings) {
	var myself = this;
	window.mfsv = this;

	this.animate = function() {
		requestAnimationFrame(myself.animate);
		myself.controls.update();
		if (myself.frameCount < myself.frameCountTarget) {
			myself.render();
			myself.frameCount++;
			//myself.log("Rendered " + myself.frameCount + "/" + myself.frameCountTarget + " frames");
		}
	};

	this.log = function(logContent) {
		console.log("[MFSViewer #"+myself.id+"]", logContent);
	}

	this.requestRender = function() {
		myself.log("Requesting new frame set!");
		myself.frameCount = 0;
	}

	this.render = function() {
		if (this.mainSceneShaderMaterial.uniforms.antiAliasing.value) {
			var xRand = Math.random() - 0.5;
			var yRand = Math.random() - 0.5;
			this.mainSceneShaderMaterial.uniforms.aaNdcOffset.value.x = 2 * xRand / (this.width);
			this.mainSceneShaderMaterial.uniforms.aaNdcOffset.value.y = 2 * yRand / (this.height);
		}

		// generate new frame from main scene
		this.renderer.render(this.mainScene, this.mainCamera, this.newFrameBuffer);
		this.mixSceneShaderMaterial.uniforms.newFrame.value = this.newFrameBuffer.texture;

		this.mixSceneShaderMaterial.uniforms.lastFrame.value = this.bufferFlipFlop ? this.secondAccumBuffer.texture : this.firstAccumBuffer.texture;
		this.mixSceneShaderMaterial.uniforms.weight.value = this.frameCount / (this.frameCount + 1);

		// mix our accum buffer with our new frame in the mix scene
		if(this.bufferFlipFlop)
			this.renderer.render(this.mixScene, this.mixCamera, this.firstAccumBuffer, false);
		else
			this.renderer.render(this.mixScene, this.mixCamera, this.secondAccumBuffer, false);

		// render the output from the mix scene to our final scene and
		this.finalQuad.material.map = !this.bufferFlipFlop ? this.secondAccumBuffer.texture : this.firstAccumBuffer.texture;
		this.renderer.render(this.finalScene, this.finalCamera);

		this.bufferFlipFlop = !this.bufferFlipFlop;
	}

	this.resize = function() {
		if (myself.fixedSize) {
				return;
		}
		var w = myself.div.offsetParent.offsetWidth, h = myself.div.offsetParent.offsetHeight;
		myself.renderer.setSize(w, h);
		w *= myself.dpr;
		h *= myself.dpr;
		myself.mainCamera.aspect = w / h;
		myself.mainCamera.updateProjectionMatrix();
		myself.firstAccumBuffer.setSize(w, h);
		myself.secondAccumBuffer.setSize(w, h);
		myself.newFrameBuffer.setSize(w, h);
		myself.mixSceneShaderMaterial.uniforms.viewport.value = new THREE.Vector2(w, h);
		myself.width = w;
		myself.height = h;
		myself.requestRender();
	}

	// set width & height
	this.fixedSize = !(settings.width == undefined || settings.height == undefined);
	this.width = settings.width ? settings.width : div.offsetParent.offsetWidth;
	this.height = settings.height ? settings.height : div.offsetParent.offsetHeight;
	window.addEventListener('resize', this.resize, false);
	this.dpr = window.devicePixelRatio;

	// misc vars
	this.frameCount = 0;
	this.frameCountTarget = 64;
	this.bufferFlipFlop = true;
	this.id = nextID++;

	// prepare renderer
	this.renderer = new THREE.WebGLRenderer( { alpha: true } );
	this.renderer.setSize(this.width, this.height);
	this.renderer.setPixelRatio(this.dpr);
	this.width = this.width * this.dpr;
	this.height = this.height * this.dpr;
	this.renderer.setClearColor(0x000000, 0);

	// set up buffers
	var texturePrecision;
	if (this.renderer.context.getExtension('WEBGL_color_buffer_float') !== null ) {
		texturePrecision = THREE.FloatType;
		this.log('FLOAT texture precision supported and will be used.');
	} else if (this.renderer.context.getExtension('EXT_color_buffer_half_float') !== null) {
		texturePrecision = THREE.HalfFloatType;
		this.log('HALFFLOAT texture precision supported and will be used.');
	} else {
		texturePrecision = THREE.UnsignedByteType;
		this.log('UNSIGNED BYTE texture precision will be used.');
	}
	var bufferSettings = {
		minFilter: THREE.NearestFilter,
		magFilter: THREE.NearestFilter,
		format: THREE.RGBAFormat,
		type: texturePrecision
	};
	this.firstAccumBuffer = new THREE.WebGLRenderTarget(this.width, this.height, bufferSettings);
	this.secondAccumBuffer = new THREE.WebGLRenderTarget(this.width, this.height, bufferSettings);
	this.newFrameBuffer = new THREE.WebGLRenderTarget(this.width, this.height, bufferSettings);

	// initialize cameras
	this.mainCamera = new THREE.PerspectiveCamera(75, this.width / this.height, 0.1, 2000);
	this.mainCamera.position.z = 7;
	this.mixCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
	this.finalCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);

	// prepare scenes
	this.mainScene = new THREE.Scene();
	this.mainScene.add(this.mainCamera);
	this.mixScene = new THREE.Scene();
	this.mixScene.add(this.mixCamera);
	this.finalScene = new THREE.Scene();
	this.finalScene.add(this.finalCamera);

	// load shaders
	var mainSceneVertexShader = " \n"+
		"// switch on high precision floats \n"+
		"#ifdef GL_ES \n"+
		"precision highp float; \n"+
		"#endif \n"+
 		"\n"+
		"varying vec3 vNormal; \n"+
		"uniform bool antiAliasing; \n"+
		"uniform vec2 aaNdcOffset; \n"+
	 	"\n"+
		"void main() { \n"+
		"		vNormal = normal; \n"+
		"		vec4 ndcVertex = projectionMatrix * modelViewMatrix * vec4(position, 1.0); \n"+
		"		if (antiAliasing) { \n"+
		"			ndcVertex.xy += aaNdcOffset * ndcVertex.w; \n"+
		"		} \n"+
		"		gl_Position = ndcVertex; \n"+
		"}";
	var mainSceneFragmentShader = " \n"+
		"// switch on high precision floats \n"+
		"#ifdef GL_ES \n"+
		"precision highp float; \n"+
		"#endif \n"+
 		"\n"+
		"varying vec3 vNormal; \n"+
 		"\n"+
		"void main() { \n"+
		"		float dProd = max(0.0, dot(vNormal, normalize(cameraPosition))); \n"+
		"		gl_FragColor = vec4(dProd, dProd, dProd, 1.0); \n"+
		"}";
	var mixSceneVertexShader = " \n"+
		"// switch on high precision floats \n"+
		"#ifdef GL_ES \n"+
		"precision highp float; \n"+
		"#endif \n"+
 		"\n"+
		"void main() { \n"+
		"		gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); \n"+
		"}";
	var mixSceneFragmentShader = " \n"+
		"// switch on high precision floats \n"+
		"#ifdef GL_ES \n"+
		"precision highp float; \n"+
		"#endif \n"+
 		"\n"+
		"uniform vec2 viewport; \n"+
		"uniform float weight; \n"+
		"uniform sampler2D newFrame; \n"+
		"uniform sampler2D lastFrame; \n"+
 		"\n"+
		"void main() { \n"+
		"		vec4 newColor = texture2D(newFrame, gl_FragCoord.xy / viewport.xy); \n"+
		"		vec4 accColor = texture2D(lastFrame, gl_FragCoord.xy / viewport.xy); \n"+
		"		gl_FragColor = mix(newColor, accColor, weight); \n"+
		"}";
	var mainSceneVertexShader = "\n"+
		"// switch on high precision floats \n"+
		"#ifdef GL_ES \n"+
		"precision highp float; \n"+
		"#endif \n"+
 		"\n"+
		"varying vec3 vNormal; \n"+
		"uniform bool antiAliasing; \n"+
		"uniform vec2 aaNdcOffset; \n"+
 		"\n"+
		"void main() { \n"+
		"		vNormal = normal; \n"+
		"		vec4 ndcVertex = projectionMatrix * modelViewMatrix * vec4(position, 1.0); \n"+
		"		if (antiAliasing) { \n"+
		"			ndcVertex.xy += aaNdcOffset * ndcVertex.w; \n"+
		"		} \n"+
		"		gl_Position = ndcVertex; \n"+
		"}";
	var mainSceneFragmentShader = "  \n"+
		"// switch on high precision floats \n"+
		"#ifdef GL_ES \n"+
		"precision highp float; \n"+
		"#endif \n"+
		"\n"+
		"varying vec3 vNormal; \n"+
		"uniform vec2 viewport; \n"+
		"uniform float weight; \n"+
		"uniform sampler2D accBuffer; \n"+
 		"\n"+
		"void main() { \n"+
		"		float dProd = max(0.0, dot(vNormal, normalize(cameraPosition))); \n"+
		"		vec4 newFragColor = vec4(vec3(dProd), 1.0); \n"+
		"		vec4 accColor = texture2D(accBuffer, gl_FragCoord.xy / viewport.xy); \n"+
		"		gl_FragColor = mix(newFragColor, accColor, weight); \n"+
		"}";
	this.mainSceneShaderMaterial = new THREE.ShaderMaterial({
		uniforms: {
			antiAliasing: { value: settings.antiAliasing != undefined ? settings.antiAliasing : true },
			aaNdcOffset: { value: new THREE.Vector2(0.0, 0.0) }
		},
		vertexShader: mainSceneVertexShader,
		fragmentShader: mainSceneFragmentShader
	});
	this.mixSceneShaderMaterial = new THREE.ShaderMaterial({
		uniforms: {
			lastFrame: { value: this.firstAccumBuffer.texture },
			newFrame: { value: this.firstAccumBuffer.texture },
			weight: { value: 0.0 },
			viewport: { value: new THREE.Vector2(this.width, this.height) }
		},
		vertexShader: mixSceneVertexShader,
		fragmentShader: mixSceneFragmentShader
	});

	// load and add our object to the scene
	var manager = new THREE.LoadingManager();
	var log = this.log, animate = this.animate;
	manager.onProgress = function (item, loaded, total) {
		log("Loaded item " + item + " (" + loaded + " of " + total + " objects)");
	};
	manager.onLoad = function () {
		log("Loading finished!");
		animate();
	};

	var loader = new THREE.OBJLoader(manager);
	loader.load(settings.objPath, function (object) {
		object.traverse(function(child) {
			if (child instanceof THREE.Mesh) {
				child.material = myself.mainSceneShaderMaterial;
			}
		} );
		myself.mainScene.add(object);
	} );

	// load quad for finalScene
	this.mixQuad = new THREE.Mesh(new THREE.PlaneBufferGeometry(2, 2), this.mixSceneShaderMaterial);
	this.mixScene.add(this.mixQuad);
	this.finalQuad = new THREE.Mesh(new THREE.PlaneBufferGeometry(2, 2), new THREE.MeshBasicMaterial( { transparent: true } ));
	this.finalScene.add(this.finalQuad);

	// attach the render-supplied DOM element
	div.appendChild(this.renderer.domElement);
	this.div = div;

	// configure trackball controls
	this.controls = new THREE.TrackballControls(this.mainCamera, this.renderer.domElement);
	this.controls.addEventListener('change', this.requestRender);
	this.controls.target.set(0, 0, 0);
	this.controls.rotateSpeed = 5.0;
	this.controls.zoomSpeed = 1.2;
	this.controls.panSpeed = 0.8;
	this.controls.noZoom = false;
	this.controls.noPan = false;
	this.controls.staticMoving = true;
	this.controls.dynamicDampingFactor = 0.3;
	this.controls.keys = [ 65, 83, 68 ];

	// set up gui
	this.gui = new dat.GUI();
	this.gui.width = 300;
	var guiOptions = {
		"ViewerID(ReadOnly)": myself.id,
		targetFrameCount: "64",
		antiAliasing: this.mainSceneShaderMaterial.uniforms.antiAliasing.value
	};
	var updateTargetFrameCount = function() {
		var newFrameCountTarget = parseFloat(guiOptions.targetFrameCount).toFixed(0);
		if (newFrameCountTarget != myself.frameCountTarget && newFrameCountTarget > 0) {
			myself.frameCountTarget = newFrameCountTarget;
			myself.requestRender();
		}
	};
	var updateRenderSettings = function () {
		myself.mainSceneShaderMaterial.uniforms.antiAliasing.value = guiOptions.antiAliasing;
		myself.requestRender();
	}
	updateTargetFrameCount();
	updateRenderSettings();
	this.gui.add(guiOptions, "ViewerID(ReadOnly)");
	this.gui.add(guiOptions, "targetFrameCount").onChange(updateTargetFrameCount);
	this.gui.add(guiOptions, "antiAliasing").onChange(updateRenderSettings);
}
<|endoftext|>


<commit_before>function midiMessageReceived( ev ) {
  var cmd = ev.data[0] >> 4;
  var channel = ev.data[0] & 0xf;
  var noteNumber = ev.data[1];
  var velocity = ev.data[2];

  if ( cmd==8 || ((cmd==9)&&(velocity==0)) ) { // with MIDI, note on with velocity zero is the same as note off
    // note off
    noteOff( noteNumber );
  } else if (cmd == 9) {
    // note on
    noteOn( noteNumber, velocity/127.0);
  } else if (cmd == 11) {
    controller( noteNumber, velocity/127.0);
  } else if (cmd == 14) {
    // pitch wheel
    pitchWheel( ((velocity * 128.0 + noteNumber)-8192)/8192.0 );
  }
}

var selectMIDI = null;
var midiAccess = null;
var midiIn = null;

function changeMIDIPort() {
  var list=midiAccess.getInputs();
  midiIn = midiAccess.getInput( list[ selectMIDI.selectedIndex ] );
  midiIn.onmessage = midiMessageReceived;
}

function selectMIDIIn( ev ) {
  var list=midiAccess.getInputs();
  var selectedIndex = ev.target.selectedIndex;

  if (list.length >= selectedIndex) {
    midiIn = midiAccess.getInput( list[selectedIndex] );
    midiIn.onmessage = midiMessageReceived;
  }
}

function onMIDIStarted( midi ) {
  midiAccess = midi;

  document.getElementById("synthbox").className = "loaded";

  selectMIDI=document.getElementById("midiIn");
  var list=midiAccess.getInputs();

  // clear the MIDI input select
  selectMIDI.options.length = 0;

  if (list.length) {
    for (var i=0; i<list.length; i++) {
      selectMIDI.options[i]=new Option(list[i].name,list[i].fingerprint,i==0,i==0);
    }
    midiIn = midiAccess.getInput( list[0] );
    midiIn.onmessage = midiMessageReceived;

    selectMIDI.onchange = selectMIDIIn;
  }
}

function onMIDISystemError( msg ) {
  document.getElementById("synthbox").className = "error";
  console.log( "Error encountered:" + msg );
}
//init: start up MIDI
window.addEventListener('load', function() {
  navigator.requestMIDIAccess( onMIDIStarted, onMIDISystemError );

});<commit_msg>Updates for new MIDI spec<commit_after>function midiMessageReceived( ev ) {
  var cmd = ev.data[0] >> 4;
  var channel = ev.data[0] & 0xf;
  var noteNumber = ev.data[1];
  var velocity = ev.data[2];

  if ( cmd==8 || ((cmd==9)&&(velocity==0)) ) { // with MIDI, note on with velocity zero is the same as note off
    // note off
    noteOff( noteNumber );
  } else if (cmd == 9) {
    // note on
    noteOn( noteNumber, velocity/127.0);
  } else if (cmd == 11) {
    controller( noteNumber, velocity/127.0);
  } else if (cmd == 14) {
    // pitch wheel
    pitchWheel( ((velocity * 128.0 + noteNumber)-8192)/8192.0 );
  }
}

var selectMIDI = null;
var midiAccess = null;
var midiIn = null;

function changeMIDIPort() {
  midiIn = midiAccess.inputs()[ selectMIDI.selectedIndex ] ;
  midiIn.onmidimessage = midiMessageReceived;
}

function selectMIDIIn( ev ) {
  var list=midiAccess.inputs();
  var selectedIndex = ev.target.selectedIndex;

  if (list.length >= selectedIndex) {
    midiIn = list[selectedIndex];
    midiIn.onmessage = midiMessageReceived;
  }
}

function onMIDIStarted( midi ) {
  midiAccess = midi;

  document.getElementById("synthbox").className = "loaded";

  selectMIDI=document.getElementById("midiIn");
  var list=midiAccess.inputs();

  // clear the MIDI input select
  selectMIDI.options.length = 0;

  if (list.length) {
    for (var i=0; i<list.length; i++) {
      selectMIDI.options[i]=new Option(list[i].name,list[i].fingerprint,i==0,i==0);
    }
    midiIn = list[0];
    midiIn.onmidimessage = midiMessageReceived;

    selectMIDI.onchange = selectMIDIIn;
  }
}

function onMIDISystemError( msg ) {
  document.getElementById("synthbox").className = "error";
  console.log( "Error encountered:" + msg );
}
//init: start up MIDI
window.addEventListener('load', function() {
  navigator.requestMIDIAccess().then( onMIDIStarted, onMIDISystemError );

});<|endoftext|>


<commit_before>var News = {
  newsMinLimit: 1,
  newsMaxLimit: 15,
  backupImage: 'img/logo-sponsor-placeholder.png',
  msgNewsLimit: 'Nyhetsantall må være et tall mellom '+this.newsMinLimit+' og '+this.newsMaxLimit,
  msgConnectionError: 'Frakoblet fra feed: ',
  msgUnsupportedFeed: 'Feeden støttes ikke',
  msgCallbackRequired: 'Callback er påkrevd, legg resultatene inn i DOMen',

  debug: 1,

  feeds: {
    // Linjeforeninger
    'berg': 'http://bergstud.no/feed/',
    'delta': 'http://org.ntnu.no/delta/wp/?feed=rss2',
    'emil': 'http://emilweb.no/feed/',
    'online': 'https://online.ntnu.no/feeds/news/',
    'nabla': 'http://nabla.no/feed/',
    'spanskrøret': 'http://spanskroret.no/feed/',
    // Medier
    'dusken': 'http://dusken.no/feed/',
    'universitetsavisa': 'http://www.universitetsavisa.no/?service=rss',
    // Store studentorganisasjoner
    'samfundet': 'http://www.samfundet.no/arrangement/rss',
    // Studentdemokrati
    'studenttinget': 'http://www.studenttinget.no/feed/',
    'velferdstinget': 'http://www.velferdstinget.no/feed/rss/',
    // NTNU
    'ntnu': 'https://www.retriever-info.com/feed/2002900/generell_arkiv166/index.xml',
    'rektoratet': 'http://www.ntnu.no/blogger/rektoratet/feed/',
  },

  // Get is called by background.html periodically, with News.unreadCount as
  // callback. Fetchfeed is also called by popup.html when requested, but
  // without the callback as we already know the amount of unread posts.
  get: function(feedName, limit, callback) {
    if (this.feeds[feedName] === undefined) {
      if (this.debug) console.log('ERROR:', this.msgUnsupportedFeed);
      callback(this.msgUnsupportedFeed);
      return;
    }
    if (isNaN(limit) || (limit < 1 && 20 < limit)) {
      if (this.debug) console.log('ERROR:', this.msgNewsLimit);
      callback(this.msgNewsLimit);
      return;
    }
    if (callback == undefined) {
      console.log('ERROR:', this.msgCallbackRequired);
      callback(this.msgCallbackRequired);
      return;
    }

    feedName = feedName.toLowerCase();
    var rssUrl = this.feeds[feedName];

    var self = this;
    $.ajax({
      url: rssUrl, // permission to use url granted in manifest.json
      dataType: 'xml',
      success: function(xml) {
        // ls.lastResponseData = xmlstring;
        self.parseFeed(xml, feedName, callback);
      },
      error: function(jqXHR, text, err) {
        if (self.debug) console.log('ERROR:', self.msgConnectionError, feedName);
        callback(self.msgConnectionError);
      },
    });
  },

  parseFeed: function(xml, feedName, callback) {
    var items = [];
    var self = this;
    $(xml).find('item').each( function() {
      var item = self.parseItem(this);
      items.push(item);
    });
    callback(items);
  },

  parseItem: function(item, feedName) {
    var post = {};
    post.title = $(item).find("title").text();
    post.link = $(item).find("link").text();
    post.description = $(item).find("description").text();
    post.creator = $(item).find("dc:creator").text();
    post.date = $(item).find("pubDate").text().substr(5, 11);

    post.id = $(item).find("guid").text().split('/')[4];
    post.image = this.backupImage;

    // Shorten 'bedriftspresentasjon' to 'bedpres'
    post.title = post.title.replace(/edrift(s)?presentasjon/gi, 'edpres');
    post.description = post.description.replace(/edrift(s)?presentasjon/gi, 'edpres');

    // Check for more direct link in the description
    var directLink = post.description.match(/(http.:\/\/)?online.ntnu.no\/event\/\d+(\/)?/g)
    if (directLink != null) {
      directLink = directLink[0];
      if (directLink != undefined) {
        post.link = directLink;
      }
    }
    // Remove HTML and excessive whitespace from description
    post.description = $.trim($(post.description).text());

    // In case browser does not grok tags with colons, stupid browser
    if (post.creator == '') {
      var tag = ("dc\\:creator").replace( /.*(\:)(.*)/, "$2" );
      $(item).find(tag).each(function(){
        post.creator = $(this).text().trim();
      });
    }
    // Didn't find a creator, set the feedname as creator
    if (post.creator == '') {
      post.creator = feedName;
    }
    // Abbreviate creators with long names
    post.creator = this.abbreviateName(post.creator);

    // title + description must not exceed 5 lines
    var line = 50; // conservative estimation
    var desclength = line * 3;
    // double line titles will shorten the description by 1 line
    if (line <= post.title.length)
      desclength -= line;
    // shorten description according to desclength
    if (desclength < post.description.length)
      post.description = post.description.substr(0, desclength) + '...';

    return post;
  },

  unreadCount: function(xmlstring) {

    return; //

    var unread_count = 0;

    // Parse the feed
    var xmldoc = $.parseXML(xmlstring);
    $xml = $(xmldoc);
    var items = $xml.find("item");
    var idList = []; // New || Updated

    // Count feed items
    items.each( function(index, element) {

      var id = $(element).find("guid").text().split('/')[4];

      // Counting...
      if (id != localStorage.mostRecentRead) {
        unread_count++;
        idList.push(id); // New || Updated
        // Send a desktop notification about the first unread element
        if (unread_count == 1) {
          if (localStorage.lastNotified != id) {
            this.showNotification(element);
          }
          localStorage.mostRecentUnread = id;
        }
      }
      // All counted :)
      else {
        if (unread_count == 0) {
          if (this.debug) console.log('no new posts');
          Browser.setBadgeText('');
        }
        else if (unread_count >= this.maxNewsAmount) {
          if (this.debug) console.log(this.maxNewsAmount + '+ unread posts');
          Browser.setBadgeText(this.maxNewsAmount + '+');
        }
        else {
          if (this.debug) console.log('1-' + (this.maxNewsAmount - 1) + ' unread posts');
          Browser.setBadgeText(String(unread_count));
        }
        localStorage.unreadCount = unread_count;
        this.storeMostRecentIds(idList); // New || Updated
        return false;
      }

      // Stop counting if unread number is greater than 9
      if (index > (this.maxNewsAmount - 1)) { // Remember index is counting 0
        if (this.debug) console.log(this.maxNewsAmount + '+ unread posts (stopped counting)');
        Browser.setBadgeText(this.maxNewsAmount + '+');
        localStorage.unreadCount = this.maxNewsAmount;
        // New or updated?
        this.storeMostRecentIds(idList); // New || Updated
        return false;
      }
    });
  },

  storeMostRecentIds: function(idList) {
    localStorage.mostRecentIdList = JSON.stringify(idList);
  },

  showNotification: function(item) {
    if (localStorage.showNotifications == 'true') {
      var post = this.parseItem(item);
      // Remember this
      localStorage.lastNotified = post.id;
      // Get content
      localStorage.notificationTitle = post.title;
      localStorage.notificationLink = post.link;
      localStorage.notificationDescription = post.description;
      localStorage.notificationCreator = post.creator; // finnes ikke alltid TOOOODOOOOOO
      localStorage.notificationDate = post.date; // finnes ikke alltid TOOOODOOOOOO
      localStorage.notificationId = post.id; // TOOOODODODOODODODODODDDOOo, bruk link
      localStorage.notificationImage = this.backupImage;
      // Show desktop notification
      Browser.createNotification('notification.html');
    }
  },

  abbreviateName: function(oldName) {
    // Abbreviate middle names if name is long
    if (18 < oldName.length) {
      var pieces = oldName.split(' ');
      if (2 < pieces.length) {
        // Add first name
        var newName = pieces[0];
        // Add one letter per middle name
        for (var i = 1; i < pieces.length - 1; i++)
          newName += ' ' + pieces[i].charAt(0).toUpperCase() + '.';
        // Add last name
        newName += ' ' + pieces[pieces.length-1];
        return newName;
      }
    }
    return oldName;
  },

  // Organization specific functions

  online_getImage: function(id, callback) {
    var image = 'undefined';
    var api = 'https://online.ntnu.no/api/f5be90e5ec1d2d454ae9/news_image_by_id/';
    $.getJSON(api + id, function(json) {
      if (json['online_news_image']) {
        image = json['online_news_image']['0']['image'];
        callback(id, image);
      }
      else {
        image = this.backupImage;
        if (this.debug) console.log('ERROR: no image exists for id: ' + id);
        callback(id, image);
      }
    })
    .error(function() {
      image = this.backupImage;
      if (this.debug) console.log('ERROR: couldn\'t connect API to get image links, returning default image');
      callback(id, image);
    });
  },

}

/*
felles:
    feeds
    get
    show notification
    fletting

hver for seg:
    // last read
    max news amount
    unread count
    parse post
    keep list of recent posts
    get post image
*/

<commit_msg>news: commenting the functions that are unfinished<commit_after>var News = {
  newsMinLimit: 1,
  newsMaxLimit: 15,
  backupImage: 'img/logo-sponsor-placeholder.png',
  msgNewsLimit: 'Nyhetsantall må være et tall mellom '+this.newsMinLimit+' og '+this.newsMaxLimit,
  msgConnectionError: 'Frakoblet fra feed: ',
  msgUnsupportedFeed: 'Feeden støttes ikke',
  msgCallbackRequired: 'Callback er påkrevd, legg resultatene inn i DOMen',

  debug: 1,

  feeds: {
    // Linjeforeninger
    'berg': 'http://bergstud.no/feed/',
    'delta': 'http://org.ntnu.no/delta/wp/?feed=rss2',
    'emil': 'http://emilweb.no/feed/',
    'online': 'https://online.ntnu.no/feeds/news/',
    'nabla': 'http://nabla.no/feed/',
    'spanskrøret': 'http://spanskroret.no/feed/',
    // Medier
    'dusken': 'http://dusken.no/feed/',
    'universitetsavisa': 'http://www.universitetsavisa.no/?service=rss',
    // Store studentorganisasjoner
    'samfundet': 'http://www.samfundet.no/arrangement/rss',
    // Studentdemokrati
    'studenttinget': 'http://www.studenttinget.no/feed/',
    'velferdstinget': 'http://www.velferdstinget.no/feed/rss/',
    // NTNU
    'ntnu': 'https://www.retriever-info.com/feed/2002900/generell_arkiv166/index.xml',
    'rektoratet': 'http://www.ntnu.no/blogger/rektoratet/feed/',
  },

  // Get is called by background.html periodically, with News.unreadCount as
  // callback. Fetchfeed is also called by popup.html when requested, but
  // without the callback as we already know the amount of unread posts.
  get: function(feedName, limit, callback) {
    if (this.feeds[feedName] === undefined) {
      if (this.debug) console.log('ERROR:', this.msgUnsupportedFeed);
      callback(this.msgUnsupportedFeed);
      return;
    }
    if (isNaN(limit) || (limit < 1 && 20 < limit)) {
      if (this.debug) console.log('ERROR:', this.msgNewsLimit);
      callback(this.msgNewsLimit);
      return;
    }
    if (callback == undefined) {
      console.log('ERROR:', this.msgCallbackRequired);
      callback(this.msgCallbackRequired);
      return;
    }

    feedName = feedName.toLowerCase();
    var rssUrl = this.feeds[feedName];

    var self = this;
    $.ajax({
      url: rssUrl, // permission to use url granted in manifest.json
      dataType: 'xml',
      success: function(xml) {
        // ls.lastResponseData = xmlstring;
        self.parseFeed(xml, feedName, callback);
      },
      error: function(jqXHR, text, err) {
        if (self.debug) console.log('ERROR:', self.msgConnectionError, feedName);
        callback(self.msgConnectionError);
      },
    });
  },

  parseFeed: function(xml, feedName, callback) {
    var items = [];
    var self = this;
    $(xml).find('item').each( function() {
      var item = self.parseItem(this);
      items.push(item);
    });
    callback(items);
  },

  // NOT DONE YET ///////////////////////////////////////////////////////////////////
  // todo: this function only handles the online-feed, make sure it handles all feeds
  parseItem: function(item, feedName) {
    var post = {};
    post.title = $(item).find("title").text();
    post.link = $(item).find("link").text();
    post.description = $(item).find("description").text();
    post.creator = $(item).find("dc:creator").text();
    post.date = $(item).find("pubDate").text().substr(5, 11);

    post.id = $(item).find("guid").text().split('/')[4];
    post.image = this.backupImage;

    // Shorten 'bedriftspresentasjon' to 'bedpres'
    post.title = post.title.replace(/edrift(s)?presentasjon/gi, 'edpres');
    post.description = post.description.replace(/edrift(s)?presentasjon/gi, 'edpres');

    // Check for more direct link in the description
    var directLink = post.description.match(/(http.:\/\/)?online.ntnu.no\/event\/\d+(\/)?/g)
    if (directLink != null) {
      directLink = directLink[0];
      if (directLink != undefined) {
        post.link = directLink;
      }
    }
    // Remove HTML and excessive whitespace from description
    post.description = $.trim($(post.description).text());

    // In case browser does not grok tags with colons, stupid browser
    if (post.creator == '') {
      var tag = ("dc\\:creator").replace( /.*(\:)(.*)/, "$2" );
      $(item).find(tag).each(function(){
        post.creator = $(this).text().trim();
      });
    }
    // Didn't find a creator, set the feedname as creator
    if (post.creator == '') {
      post.creator = feedName;
    }
    // Abbreviate creators with long names
    post.creator = this.abbreviateName(post.creator);

    // title + description must not exceed 5 lines
    var line = 50; // conservative estimation
    var desclength = line * 3;
    // double line titles will shorten the description by 1 line
    if (line <= post.title.length)
      desclength -= line;
    // shorten description according to desclength
    if (desclength < post.description.length)
      post.description = post.description.substr(0, desclength) + '...';

    return post;
  },

  // NOT DONE YET ///////////////////////////////////////////////////////////////////
  // todo: this function only handles the online-feed, make sure it handles all feeds
  unreadCount: function(xmlstring) {

    return; //

    var unread_count = 0;

    // Parse the feed
    var xmldoc = $.parseXML(xmlstring);
    $xml = $(xmldoc);
    var items = $xml.find("item");
    var idList = []; // New || Updated

    // Count feed items
    items.each( function(index, element) {

      var id = $(element).find("guid").text().split('/')[4];

      // Counting...
      if (id != localStorage.mostRecentRead) {
        unread_count++;
        idList.push(id); // New || Updated
        // Send a desktop notification about the first unread element
        if (unread_count == 1) {
          if (localStorage.lastNotified != id) {
            this.showNotification(element);
          }
          localStorage.mostRecentUnread = id;
        }
      }
      // All counted :)
      else {
        if (unread_count == 0) {
          if (this.debug) console.log('no new posts');
          Browser.setBadgeText('');
        }
        else if (unread_count >= this.maxNewsAmount) {
          if (this.debug) console.log(this.maxNewsAmount + '+ unread posts');
          Browser.setBadgeText(this.maxNewsAmount + '+');
        }
        else {
          if (this.debug) console.log('1-' + (this.maxNewsAmount - 1) + ' unread posts');
          Browser.setBadgeText(String(unread_count));
        }
        localStorage.unreadCount = unread_count;
        this.storeMostRecentIds(idList); // New || Updated
        return false;
      }

      // Stop counting if unread number is greater than 9
      if (index > (this.maxNewsAmount - 1)) { // Remember index is counting 0
        if (this.debug) console.log(this.maxNewsAmount + '+ unread posts (stopped counting)');
        Browser.setBadgeText(this.maxNewsAmount + '+');
        localStorage.unreadCount = this.maxNewsAmount;
        // New or updated?
        this.storeMostRecentIds(idList); // New || Updated
        return false;
      }
    });
  },

  storeMostRecentIds: function(idList) {
    localStorage.mostRecentIdList = JSON.stringify(idList);
  },

  // NOT DONE YET ///////////////////////////////////////////////////////////////////
  // todo: this function only handles the online-feed, make sure it handles all feeds
  showNotification: function(item) {
    if (localStorage.showNotifications == 'true') {
      var post = this.parseItem(item);
      // Remember this
      localStorage.lastNotified = post.id;
      // Get content
      localStorage.notificationTitle = post.title;
      localStorage.notificationLink = post.link;
      localStorage.notificationDescription = post.description;
      localStorage.notificationCreator = post.creator; // finnes ikke alltid TOOOODOOOOOO
      localStorage.notificationDate = post.date; // finnes ikke alltid TOOOODOOOOOO
      localStorage.notificationId = post.id; // TOOOODODODOODODODODODDDOOo, bruk link
      localStorage.notificationImage = this.backupImage;
      // Show desktop notification
      Browser.createNotification('notification.html');
    }
  },

  abbreviateName: function(oldName) {
    // Abbreviate middle names if name is long
    if (18 < oldName.length) {
      var pieces = oldName.split(' ');
      if (2 < pieces.length) {
        // Add first name
        var newName = pieces[0];
        // Add one letter per middle name
        for (var i = 1; i < pieces.length - 1; i++)
          newName += ' ' + pieces[i].charAt(0).toUpperCase() + '.';
        // Add last name
        newName += ' ' + pieces[pieces.length-1];
        return newName;
      }
    }
    return oldName;
  },

  // Organization specific functions

  online_getImage: function(id, callback) {
    var image = 'undefined';
    var api = 'https://online.ntnu.no/api/f5be90e5ec1d2d454ae9/news_image_by_id/';
    $.getJSON(api + id, function(json) {
      if (json['online_news_image']) {
        image = json['online_news_image']['0']['image'];
        callback(id, image);
      }
      else {
        image = this.backupImage;
        if (this.debug) console.log('ERROR: no image exists for id: ' + id);
        callback(id, image);
      }
    })
    .error(function() {
      image = this.backupImage;
      if (this.debug) console.log('ERROR: couldn\'t connect API to get image links, returning default image');
      callback(id, image);
    });
  },

}

/*
felles:
    feeds
    get
    parseFeed
    parseItem
    showNotification
    unreadCount
    fletting

hver for seg:
    // last read
    keep list of recent posts
    get post image
*/

<|endoftext|>


<commit_before>(function(a){function b(a,b,c){return a.slice(b*c,b*c+c)}a.fn.simplePagingGrid=function(c){var d=a.extend({buttonBarTemplate:'
<button class="btn">« Prev</button><button class="btn pull-right">Next &raquo</button>

',tableTemplate:"

",headerTemplate:'{{title}}	',sortableHeaderTemplate:'

{{title}}	',emptyCellTemplate:" 	",loadingOverlayTemplate:'

'},c.templates);var e=a.extend({pageSize:10,columnWidths:[],cellTemplates:null,headerTemplates:null,sortable:[],sortOrder:"asc",initialSortColumn:null,tableClass:"table",dataFunction:null,dataUrl:null,data:null,minimumVisibleRows:10,showLoadingOverlay:true},c);e.templates=d;return this.each(function(){function t(){if(n){k.attr("disabled","disabled");l.attr("disabled","disabled")}else{if(i===0){k.attr("disabled","disabled")}else{k.removeAttr("disabled")}if(m.length<e.pageSize){l.attr("disabled","disabled")}else{l.removeAttr("disabled")}}}function u(){if(r!=null){r.width(s.width());r.height(s.height())}}function v(){if(e.showLoadingOverlay){r=a(e.templates.loadingOverlayTemplate);u();s.prepend(r)}}function w(){if(r!==null){r.remove();r=null}}function x(c){var d;var f;var g;if(c!==undefined){e.dataUrl=c;i=0}if(e.data!==null){d=p===null?e.data:e.data.sort(function(b,c){f=o==="asc"?b[p]:c[p];g=o==="asc"?c[p]:b[p];if(a.isNumeric(f)){if(f<g){return 1}else if(f>g){return-1}return 0}return f.localeCompare(g)});m=b(d,i,e.pageSize);y();t()}else if(e.dataUrl!==null){n=true;t();v();a.getJSON(e.dataUrl,{page:i,pageSize:e.pageSize,sortColumn:p,sortOrder:o},function(a){m=a;y();n=false;t();w()})}else if(e.dataFunction!==null){m=e.dataFunction(i,e.pageSize,p,o);y();t()}}function y(){d.empty();a.each(m,function(b,c){var f=a("
");a.each(e.columnKeys,function(b,d){var g=a("");if(e.cellTemplates!==null&&b<e.cellTemplates.length&&e.cellTemplates[b]!==null){g.html(Mustache.render(e.cellTemplates[b],c))}else{g.html(c[d])}f.append(g)});d.append(f)});if(m.length<e.minimumVisibleRows){var b;var c;for(b=m.length;b<e.minimumVisibleRows;b++){c=a("
");a.each(e.columnKeys,function(){c.append(Mustache.render(e.templates.emptyCellTemplate))});d.append(c)}}}var c;var d;var f;var g;var h;var i=0;var j;var k;var l;var m;var n=false;var o=e.sortOrder;var p=e.initialSortColumn;var q=null;var r=null;var s=a(this);c=a(e.templates.tableTemplate);f=c.find("thead");g=a("
").appendTo(f);d=c.find("tbody");a.each(e.columnNames,function(b,c){var d=e.sortable[b];var f;var h;var i=e.columnKeys[b];var j;var k=null;j=e.columnWidths.length>b?e.columnWidths[b]:"";if(e.headerTemplates!==null&&b<e.headerTemplates.length&&e.headerTemplates[b]!=null){k=a(Mustache.render(e.headerTemplates[b],{width:j,title:c}))}if(d){if(k===null){k=a(Mustache.render(e.templates.sortableHeaderTemplate,{width:j,title:c}))}f=k.find(".sort-ascending");h=k.find(".sort-descending");function l(a){a.preventDefault();if(p===i){o=o==="asc"?"desc":"asc"}p=i;if(q!=null){q.removeClass("sort-ascending-active");q.removeClass("sort-descending-active")}q=o==="asc"?f:h;q.addClass(o==="asc"?"sort-ascending-active":"sort-descending-active");x()}f.click(function(a){l(a)});h.click(function(a){l(a)})}else{if(k===null){k=a(Mustache.render(e.templates.headerTemplate,{width:j,title:c}))}}g.append(k)});c.addClass(e.tableClass);buttonBarHtml=e.templates.buttonBarTemplate;j=a(buttonBarHtml);k=j.find("button").first();l=j.find("button").last();k.click(function(a){a.preventDefault();if(i>0){i--;x()}});l.click(function(a){a.preventDefault();if(m.length===e.pageSize){i++;x()}});x();s.append(c);s.append(j);a(window).resize(u);this.refreshData=x;return this})}})(jQuery)<commit_msg>Bringing minified JavaScript inline with full fat version<commit_after>(function(a){function b(a,b,c){return a.slice(b*c,b*c+c)}a.fn.simplePagingGrid=function(c){var d=a.extend({buttonBarTemplate:'
<button class="btn pull-left">« Prev</button><button class="btn pull-right">Next &raquo</button>

',tableTemplate:"

",headerTemplate:'{{title}}	',sortableHeaderTemplate:'

{{title}}	',emptyCellTemplate:" 	",loadingOverlayTemplate:'

',currentPageTemplate:'{{pageNumber}}',pageLinkTemplate:'{{pageNumber}}'},c.templates);var e=a.extend({pageSize:10,columnWidths:[],cellTemplates:null,headerTemplates:null,sortable:[],sortOrder:"asc",initialSortColumn:null,tableClass:"table",dataFunction:null,dataUrl:null,data:null,minimumVisibleRows:10,showLoadingOverlay:true,showPageNumbers:true,numberOfPageLinks:20},c);e.templates=d;return this.each(function(){function v(){return Math.ceil(n/e.pageSize)}function w(){if(o){k.attr("disabled","disabled");l.attr("disabled","disabled")}else{if(i===0){k.attr("disabled","disabled")}else{k.removeAttr("disabled")}if(n===null&&m.length<e.pageSize||n!==null&&i>=v()-1){l.attr("disabled","disabled")}else{l.removeAttr("disabled")}}}function x(){function b(a){return function(b){b.preventDefault();i=a-1;C()}}if(e.showPageNumbers&&n!==null){var c;var d;var f=v();var g=[];var h;var k;c=i+1-e.numberOfPageLinks/2;if(c<1){c=1;d=e.numberOfPageLinks;if(d>f){d=f}}else{d=i+1+e.numberOfPageLinks/2-1;if(d>f){d=f;c=d-e.numberOfPageLinks+1;if(c<1)c=1}}var l=j.find(".page-numbers");l.empty();for(h=c;h<=d;h++){if(h===i+1){k=a(Mustache.render(e.templates.currentPageTemplate,{pageNumber:h}))}else{k=a(Mustache.render(e.templates.pageLinkTemplate,{pageNumber:h}));k.click(b(h))}l.append(k)}}}function y(){if(s!=null){s.width(u.width());s.height(u.height())}}function z(){if(e.showLoadingOverlay){s=a(e.templates.loadingOverlayTemplate);y();u.prepend(s)}}function A(){if(s!==null){s.remove();s=null}}function B(b){if(a.isArray(b)){m=b}else if(a.isPlainObject(b)){m=b.currentPage;n=b.totalRows}}function C(c){var d;var f;var g;var h;if(c!==undefined){e.dataUrl=c;i=0}if(e.data!==null){if(a.isArray(e.data)){h=e.data}else if(a.isPlainObject(e.data)){h=e.data.currentPage;n=e.data.totalRows}d=q===null?h:h.sort(function(b,c){f=p==="asc"?b[q]:c[q];g=p==="asc"?c[q]:b[q];if(a.isNumeric(f)){if(f<g){return 1}else if(f>g){return-1}return 0}return f.localeCompare(g)});m=b(d,i,e.pageSize);t.currentData=m;D();w();x()}else if(e.dataUrl!==null){o=true;w();z();a.getJSON(e.dataUrl,{page:i,pageSize:e.pageSize,sortColumn:q,sortOrder:p},function(a){B(a);t.currentData=m;D();o=false;w();x();A()})}else if(e.dataFunction!==null){B(e.dataFunction(i,e.pageSize,q,p));t.currentData=m;D();w();x()}}function D(){d.empty();a.each(m,function(b,c){var f=a("
");a.each(e.columnKeys,function(b,d){var g=a("");if(e.cellTemplates!==null&&b<e.cellTemplates.length&&e.cellTemplates[b]!==null){g.html(Mustache.render(e.cellTemplates[b],c))}else{g.html(c[d])}f.append(g)});d.append(f)});if(m.length<e.minimumVisibleRows){var b;var c;for(b=m.length;b<e.minimumVisibleRows;b++){c=a("
");a.each(e.columnKeys,function(){c.append(Mustache.render(e.templates.emptyCellTemplate))});d.append(c)}}}var c;var d;var f;var g;var h;var i=0;var j;var k;var l;var m;var n=null;var o=false;var p=e.sortOrder;var q=e.initialSortColumn;var r=null;var s=null;var t=this;var u=a(this);c=a(e.templates.tableTemplate);f=c.find("thead");g=a("
").appendTo(f);d=c.find("tbody");a.each(e.columnNames,function(b,c){var d=e.sortable[b];var f;var h;var i=e.columnKeys[b];var j;var k=null;j=e.columnWidths.length>b?e.columnWidths[b]:"";if(e.headerTemplates!==null&&b<e.headerTemplates.length&&e.headerTemplates[b]!=null){k=a(Mustache.render(e.headerTemplates[b],{width:j,title:c}))}if(d){if(k===null){k=a(Mustache.render(e.templates.sortableHeaderTemplate,{width:j,title:c}))}f=k.find(".sort-ascending");h=k.find(".sort-descending");function l(a){a.preventDefault();if(q===i){p=p==="asc"?"desc":"asc"}q=i;if(r!=null){r.removeClass("sort-ascending-active");r.removeClass("sort-descending-active")}r=p==="asc"?f:h;r.addClass(p==="asc"?"sort-ascending-active":"sort-descending-active");C()}f.click(function(a){l(a)});h.click(function(a){l(a)})}else{if(k===null){k=a(Mustache.render(e.templates.headerTemplate,{width:j,title:c}))}}g.append(k)});c.addClass(e.tableClass);buttonBarHtml=e.templates.buttonBarTemplate;j=a(buttonBarHtml);k=j.find("button").first();l=j.find("button").last();k.click(function(a){a.preventDefault();if(i>0){i--;C()}});l.click(function(a){a.preventDefault();if(m.length===e.pageSize){i++;C()}});C();u.append(c);u.append(j);a(window).resize(y);t.refreshData=C;return this})}})(jQuery)<|endoftext|>


<commit_before>'use strict';

//  ---------------------------------------------------------------------------

const Exchange = require ('./base/Exchange');
const { ExchangeError } = require ('./base/errors');

//  ---------------------------------------------------------------------------

module.exports = class nova extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'nova',
            'name': 'Novaexchange',
            'countries': [ 'TZ' ], // Tanzania
            'rateLimit': 2000,
            'version': 'v2',
            'has': {
                'CORS': false,
                'createMarketOrder': false,
                'createDepositAddress': true,
                'fetchDepositAddress': true,
                'fetchDeposits': true,
                'fetchWithdrawals': true,
            },
            'urls': {
                'referral': 'https://novaexchange.com/signup/?re=is8vz2hsl3qxewv1uawd',
                'logo': 'https://user-images.githubusercontent.com/1294454/30518571-78ca0bca-9b8a-11e7-8840-64b83a4a94b2.jpg',
                'api': 'https://novaexchange.com/remote',
                'www': 'https://novaexchange.com',
                'doc': 'https://novaexchange.com/remote/faq',
            },
            'api': {
                'public': {
                    'get': [
                        'markets/',
                        'markets/{basecurrency}/',
                        'market/info/{pair}/',
                        'market/orderhistory/{pair}/',
                        'market/openorders/{pair}/buy/',
                        'market/openorders/{pair}/sell/',
                        'market/openorders/{pair}/both/',
                        'market/openorders/{pair}/{ordertype}/',
                    ],
                },
                'private': {
                    'post': [
                        'getbalances/',
                        'getbalance/{currency}/',
                        'getdeposits/',
                        'getwithdrawals/',
                        'getnewdepositaddress/{currency}/',
                        'getdepositaddress/{currency}/',
                        'myopenorders/',
                        'myopenorders_market/{pair}/',
                        'cancelorder/{orderid}/',
                        'withdraw/{currency}/',
                        'trade/{pair}/',
                        'tradehistory/',
                        'getdeposithistory/',
                        'getwithdrawalhistory/',
                        'walletstatus/',
                        'walletstatus/{currency}/',
                    ],
                },
            },
        });
    }

    async fetchMarkets (params = {}) {
        const response = await this.publicGetMarkets (params);
        const markets = response['markets'];
        const result = [];
        for (let i = 0; i < markets.length; i++) {
            const market = markets[i];
            const id = this.safeString (market, 'marketname');
            const [ quoteId, baseId ] = id.split ('_');
            const base = this.safeCurrencyCode (baseId);
            const quote = this.safeCurrencyCode (quoteId);
            const symbol = base + '/' + quote;
            const disabled = this.safeValue (market, 'disabled', false);
            const active = !disabled;
            result.push ({
                'id': id,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'baseId': baseId,
                'quoteId': quoteId,
                'active': active,
                'info': market,
            });
        }
        return result;
    }

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        await this.loadMarkets ();
        const request = {
            'pair': this.marketId (symbol),
        };
        const response = await this.publicGetMarketOpenordersPairBoth (this.extend (request, params));
        return this.parseOrderBook (response, undefined, 'buyorders', 'sellorders', 'price', 'amount');
    }

    async fetchTicker (symbol, params = {}) {
        await this.loadMarkets ();
        const request = {
            'pair': this.marketId (symbol),
        };
        const response = await this.publicGetMarketInfoPair (this.extend (request, params));
        const ticker = response['markets'][0];
        const timestamp = this.milliseconds ();
        const last = this.safeFloat (ticker, 'last_price');
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': this.safeFloat (ticker, 'high24h'),
            'low': this.safeFloat (ticker, 'low24h'),
            'bid': this.safeFloat (ticker, 'bid'),
            'bidVolume': undefined,
            'ask': this.safeFloat (ticker, 'ask'),
            'askVolume': undefined,
            'vwap': undefined,
            'open': undefined,
            'close': last,
            'last': last,
            'previousClose': undefined,
            'change': undefined,
            'percentage': this.safeFloat (ticker, 'change24h'),
            'average': undefined,
            'baseVolume': undefined,
            'quoteVolume': this.safeFloat (ticker, 'volume24h'),
            'info': ticker,
        };
    }

    parseTrade (trade, market = undefined) {
        let timestamp = this.safeInteger (trade, 'unix_t_datestamp');
        if (timestamp !== undefined) {
            timestamp *= 1000;
        }
        let symbol = undefined;
        if (market !== undefined) {
            symbol = market['symbol'];
        }
        const type = undefined;
        let side = this.safeString (trade, 'tradetype');
        if (side !== undefined) {
            side = side.toLowerCase ();
        }
        const price = this.safeFloat (trade, 'price');
        const amount = this.safeFloat (trade, 'amount');
        let cost = undefined;
        if (price !== undefined) {
            if (amount !== undefined) {
                cost = amount * price;
            }
        }
        return {
            'id': undefined,
            'info': trade,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'symbol': symbol,
            'order': undefined,
            'type': type,
            'side': side,
            'takerOrMaker': undefined,
            'price': price,
            'amount': amount,
            'cost': cost,
            'fee': undefined,
        };
    }

    async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        const market = this.market (symbol);
        const request = {
            'pair': market['id'],
        };
        const response = await this.publicGetMarketOrderhistoryPair (this.extend (request, params));
        return this.parseTrades (response['items'], market, since, limit);
    }

    async fetchBalance (params = {}) {
        await this.loadMarkets ();
        const response = await this.privatePostGetbalances (params);
        const balances = this.safeValue (response, 'balances');
        const result = { 'info': response };
        for (let i = 0; i < balances.length; i++) {
            const balance = balances[i];
            const currencyId = this.safeString (balance, 'currency');
            const code = this.safeCurrencyCode (currencyId);
            const lockbox = this.safeFloat (balance, 'amount_lockbox');
            const trades = this.safeFloat (balance, 'amount_trades');
            const account = {
                'free': this.safeFloat (balance, 'amount'),
                'used': this.sum (lockbox, trades),
                'total': this.safeFloat (balance, 'amount_total'),
            };
            result[code] = account;
        }
        return this.parseBalance (result);
    }

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        if (type === 'market') {
            throw new ExchangeError (this.id + ' allows limit orders only');
        }
        await this.loadMarkets ();
        amount = amount.toString ();
        price = price.toString ();
        const market = this.market (symbol);
        const request = {
            'tradetype': side.toUpperCase (),
            'tradeamount': amount,
            'tradeprice': price,
            'tradebase': 1,
            'pair': market['id'],
        };
        const response = await this.privatePostTradePair (this.extend (request, params));
        const tradeItems = this.safeValue (response, 'tradeitems', []);
        const tradeItemsByType = this.indexBy (tradeItems, 'type');
        const created = this.safeValue (tradeItemsByType, 'created', {});
        const orderId = this.safeString (created, 'orderid');
        return {
            'info': response,
            'id': orderId,
        };
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        const request = {
            'orderid': id,
        };
        return await this.privatePostCancelorder (this.extend (request, params));
    }

    async createDepositAddress (code, params = {}) {
        await this.loadMarkets ();
        const currency = this.currency (code);
        const request = {
            'currency': currency['id'],
        };
        const response = await this.privatePostGetnewdepositaddressCurrency (this.extend (request, params));
        const address = this.safeString (response, 'address');
        this.checkAddress (address);
        const tag = this.safeString (response, 'tag');
        return {
            'currency': code,
            'address': address,
            'tag': tag,
            'info': response,
        };
    }

    async fetchDepositAddress (code, params = {}) {
        await this.loadMarkets ();
        const currency = this.currency (code);
        const request = {
            'currency': currency['id'],
        };
        const response = await this.privatePostGetdepositaddressCurrency (this.extend (request, params));
        const address = this.safeString (response, 'address');
        this.checkAddress (address);
        const tag = this.safeString (response, 'tag');
        return {
            'currency': code,
            'address': address,
            'tag': tag,
            'info': response,
        };
    }

    parseTransaction (transaction, currency = undefined) {
        let timestamp = this.safeInteger2 (transaction, 'unix_t_time_seen', 'unix_t_daterequested');
        if (timestamp !== undefined) {
            timestamp *= 1000;
        }
        let updated = this.safeInteger (transaction, 'unix_t_datesent');
        if (updated !== undefined) {
            updated *= 1000;
        }
        const currencyId = this.safeString (transaction, 'currency');
        const code = this.safeCurrencyCode (currencyId);
        const status = this.parseTransactionStatus (this.safeString (transaction, 'status'));
        const amount = this.safeFloat (transaction, 'tx_amount');
        const address = this.safeString (transaction, 'tx_address');
        const fee = undefined;
        const txid = this.safeString (transaction, 'tx_txid');
        const type = this.safeString (transaction, 'type');
        return {
            'info': transaction,
            'id': undefined,
            'currency': code,
            'amount': amount,
            'address': address,
            'tag': undefined,
            'status': status,
            'type': type,
            'updated': updated,
            'txid': txid,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'fee': fee,
        };
    }

    parseTransactionStatus (status) {
        const statuses = {
            'Accounted': 'ok',
            'Confirmed': 'ok',
            'Incoming': 'pending',
            'Approved': 'pending',
        };
        return this.safeString (statuses, status, status);
    }

    async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) {
        const response = await this.privatePostGetdeposithistory (params);
        for (let i = 0; i < response['items'].length; i++) {
            response['items'][i]['type'] = 'deposit';
        }
        let currency = undefined;
        if (code !== undefined) {
            currency = this.currency (code);
        }
        const deposits = this.safeValue (response, 'items', []);
        return this.parseTransactions (deposits, currency, since, limit);
    }

    async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) {
        const response = await this.privatePostGetwithdrawalhistory (params);
        for (let i = 0; i < response['items'].length; i++) {
            response['items'][i]['type'] = 'withdrawal';
        }
        let currency = undefined;
        if (code !== undefined) {
            currency = this.currency (code);
        }
        const withdrawals = this.safeValue (response, 'items', []);
        return this.parseTransactions (withdrawals, currency, since, limit);
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let url = this.urls['api'] + '/' + this.version + '/';
        if (api === 'private') {
            url += api + '/';
        }
        url += this.implodeParams (path, params);
        const query = this.omit (params, this.extractParams (path));
        if (api === 'public') {
            if (Object.keys (query).length) {
                url += '?' + this.urlencode (query);
            }
        } else {
            this.checkRequiredCredentials ();
            const nonce = this.nonce ().toString ();
            url += '?' + this.urlencode ({ 'nonce': nonce });
            const signature = this.hmac (this.encode (url), this.encode (this.secret), 'sha512', 'base64');
            body = this.urlencode (this.extend ({
                'apikey': this.apiKey,
                'signature': signature,
            }, query));
            headers = {
                'Content-Type': 'application/x-www-form-urlencoded',
            };
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }

    async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        const response = await this.fetch2 (path, api, method, params, headers, body);
        if ('status' in response) {
            if (response['status'] !== 'success') {
                throw new ExchangeError (this.id + ' ' + this.json (response));
            }
        }
        return response;
    }
};
<commit_msg>Update nova.js<commit_after>'use strict';

//  ---------------------------------------------------------------------------

const Exchange = require ('./base/Exchange');
const { ExchangeError } = require ('./base/errors');

//  ---------------------------------------------------------------------------

module.exports = class nova extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'nova',
            'name': 'Novaexchange',
            'countries': [ 'TZ' ], // Tanzania
            'rateLimit': 2000,
            'version': 'v2',
            'has': {
                'CORS': false,
                'createMarketOrder': false,
                'createDepositAddress': true,
                'fetchDepositAddress': true,
                'fetchDeposits': true,
                'fetchWithdrawals': true,
            },
            'urls': {
                'referral': 'https://novaexchange.com/signup/?re=is8vz2hsl3qxewv1uawd',
                'logo': 'https://user-images.githubusercontent.com/1294454/30518571-78ca0bca-9b8a-11e7-8840-64b83a4a94b2.jpg',
                'api': 'https://novaexchange.com/remote',
                'www': 'https://novaexchange.com',
                'doc': 'https://novaexchange.com/remote/faq',
            },
            'api': {
                'public': {
                    'get': [
                        'markets/',
                        'markets/{basecurrency}/',
                        'market/info/{pair}/',
                        'market/orderhistory/{pair}/',
                        'market/openorders/{pair}/buy/',
                        'market/openorders/{pair}/sell/',
                        'market/openorders/{pair}/both/',
                        'market/openorders/{pair}/{ordertype}/',
                    ],
                },
                'private': {
                    'post': [
                        'getbalances/',
                        'getbalance/{currency}/',
                        'getdeposits/',
                        'getwithdrawals/',
                        'getnewdepositaddress/{currency}/',
                        'getdepositaddress/{currency}/',
                        'myopenorders/',
                        'myopenorders_market/{pair}/',
                        'cancelorder/{orderid}/',
                        'withdraw/{currency}/',
                        'trade/{pair}/',
                        'tradehistory/',
                        'getdeposithistory/',
                        'getwithdrawalhistory/',
                        'walletstatus/',
                        'walletstatus/{currency}/',
                    ],
                },
            },
        });
    }

    async fetchMarkets (params = {}) {
        const response = await this.publicGetMarkets (params);
        const markets = response['markets'];
        const result = [];
        for (let i = 0; i < markets.length; i++) {
            const market = markets[i];
            const id = this.safeString (market, 'marketname');
            const [ quoteId, baseId ] = id.split ('_');
            const base = this.safeCurrencyCode (baseId);
            const quote = this.safeCurrencyCode (quoteId);
            const symbol = base + '/' + quote;
            const disabled = this.safeValue (market, 'disabled', false);
            const active = !disabled;
            result.push ({
                'id': id,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'baseId': baseId,
                'quoteId': quoteId,
                'active': active,
                'info': market,
            });
        }
        return result;
    }

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        await this.loadMarkets ();
        const request = {
            'pair': this.marketId (symbol),
        };
        const response = await this.publicGetMarketOpenordersPairBoth (this.extend (request, params));
        return this.parseOrderBook (response, undefined, 'buyorders', 'sellorders', 'price', 'amount');
    }

    async fetchTicker (symbol, params = {}) {
        await this.loadMarkets ();
        const request = {
            'pair': this.marketId (symbol),
        };
        const response = await this.publicGetMarketInfoPair (this.extend (request, params));
        const ticker = response['markets'][0];
        const timestamp = this.milliseconds ();
        const last = this.safeFloat (ticker, 'last_price');
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': this.safeFloat (ticker, 'high24h'),
            'low': this.safeFloat (ticker, 'low24h'),
            'bid': this.safeFloat (ticker, 'bid'),
            'bidVolume': undefined,
            'ask': this.safeFloat (ticker, 'ask'),
            'askVolume': undefined,
            'vwap': undefined,
            'open': undefined,
            'close': last,
            'last': last,
            'previousClose': undefined,
            'change': undefined,
            'percentage': this.safeFloat (ticker, 'change24h'),
            'average': undefined,
            'baseVolume': undefined,
            'quoteVolume': this.safeFloat (ticker, 'volume24h'),
            'info': ticker,
        };
    }

    parseTrade (trade, market = undefined) {
        let timestamp = this.safeInteger (trade, 'unix_t_datestamp');
        if (timestamp !== undefined) {
            timestamp *= 1000;
        }
        let symbol = undefined;
        if (market !== undefined) {
            symbol = market['symbol'];
        }
        const type = undefined;
        let side = this.safeString (trade, 'tradetype');
        if (side !== undefined) {
            side = side.toLowerCase ();
        }
        const price = this.safeFloat (trade, 'price');
        const amount = this.safeFloat (trade, 'amount');
        let cost = undefined;
        if (price !== undefined) {
            if (amount !== undefined) {
                cost = amount * price;
            }
        }
        return {
            'id': undefined,
            'info': trade,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'symbol': symbol,
            'order': undefined,
            'type': type,
            'side': side,
            'takerOrMaker': undefined,
            'price': price,
            'amount': amount,
            'cost': cost,
            'fee': undefined,
        };
    }

    async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        const market = this.market (symbol);
        const request = {
            'pair': market['id'],
        };
        const response = await this.publicGetMarketOrderhistoryPair (this.extend (request, params));
        return this.parseTrades (response['items'], market, since, limit);
    }

    async fetchBalance (params = {}) {
        await this.loadMarkets ();
        const response = await this.privatePostGetbalances (params);
        const balances = this.safeValue (response, 'balances');
        const result = { 'info': response };
        for (let i = 0; i < balances.length; i++) {
            const balance = balances[i];
            const currencyId = this.safeString (balance, 'currency');
            const code = this.safeCurrencyCode (currencyId);
            const lockbox = this.safeFloat (balance, 'amount_lockbox');
            const trades = this.safeFloat (balance, 'amount_trades');
            const account = {
                'free': this.safeFloat (balance, 'amount'),
                'used': this.sum (lockbox, trades),
                'total': this.safeFloat (balance, 'amount_total'),
            };
            result[code] = account;
        }
        return this.parseBalance (result);
    }

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        if (type === 'market') {
            throw new ExchangeError (this.id + ' allows limit orders only');
        }
        await this.loadMarkets ();
        amount = amount.toString ();
        price = price.toString ();
        const market = this.market (symbol);
        const request = {
            'tradetype': side.toUpperCase (),
            'tradeamount': amount,
            'tradeprice': price,
            'tradebase': 1,
            'pair': market['id'],
        };
        const response = await this.privatePostTradePair (this.extend (request, params));
        const tradeItems = this.safeValue (response, 'tradeitems', []);
        const tradeItemsByType = this.indexBy (tradeItems, 'type');
        const created = this.safeValue (tradeItemsByType, 'created', {});
        const orderId = this.safeString (created, 'orderid');
        return {
            'info': response,
            'id': orderId,
        };
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        const request = {
            'orderid': id,
        };
        return await this.privatePostCancelorder (this.extend (request, params));
    }

    async createDepositAddress (code, params = {}) {
        await this.loadMarkets ();
        const currency = this.currency (code);
        const request = {
            'currency': currency['id'],
        };
        const response = await this.privatePostGetnewdepositaddressCurrency (this.extend (request, params));
        const address = this.safeString (response, 'address');
        this.checkAddress (address);
        const tag = this.safeString (response, 'tag');
        return {
            'currency': code,
            'address': address,
            'tag': tag,
            'info': response,
        };
    }

    async fetchDepositAddress (code, params = {}) {
        await this.loadMarkets ();
        const currency = this.currency (code);
        const request = {
            'currency': currency['id'],
        };
        const response = await this.privatePostGetdepositaddressCurrency (this.extend (request, params));
        const address = this.safeString (response, 'address');
        this.checkAddress (address);
        const tag = this.safeString (response, 'tag');
        return {
            'currency': code,
            'address': address,
            'tag': tag,
            'info': response,
        };
    }

    parseTransaction (transaction, currency = undefined) {
        let timestamp = this.safeInteger2 (transaction, 'unix_t_time_seen', 'unix_t_daterequested');
        if (timestamp !== undefined) {
            timestamp *= 1000;
        }
        let updated = this.safeInteger (transaction, 'unix_t_datesent');
        if (updated !== undefined) {
            updated *= 1000;
        }
        const currencyId = this.safeString (transaction, 'currency');
        const code = this.safeCurrencyCode (currencyId);
        const status = this.parseTransactionStatus (this.safeString (transaction, 'status'));
        const amount = this.safeFloat (transaction, 'tx_amount');
        const address = this.safeString (transaction, 'tx_address');
        const fee = undefined;
        const txid = this.safeString (transaction, 'tx_txid');
        const type = this.safeString (transaction, 'type');
        return {
            'info': transaction,
            'id': undefined,
            'currency': code,
            'amount': amount,
            'address': address,
            'tag': undefined,
            'status': status,
            'type': type,
            'updated': updated,
            'txid': txid,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'fee': fee,
        };
    }

    parseTransactionStatus (status) {
        const statuses = {
            'Accounted': 'ok',
            'Confirmed': 'ok',
            'Sent': 'ok',
            'Incoming': 'pending',
            'Approved': 'pending',
        };
        return this.safeString (statuses, status, status);
    }

    async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) {
        const response = await this.privatePostGetdeposithistory (params);
        for (let i = 0; i < response['items'].length; i++) {
            response['items'][i]['type'] = 'deposit';
        }
        let currency = undefined;
        if (code !== undefined) {
            currency = this.currency (code);
        }
        const deposits = this.safeValue (response, 'items', []);
        return this.parseTransactions (deposits, currency, since, limit);
    }

    async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) {
        const response = await this.privatePostGetwithdrawalhistory (params);
        for (let i = 0; i < response['items'].length; i++) {
            response['items'][i]['type'] = 'withdrawal';
        }
        let currency = undefined;
        if (code !== undefined) {
            currency = this.currency (code);
        }
        const withdrawals = this.safeValue (response, 'items', []);
        return this.parseTransactions (withdrawals, currency, since, limit);
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let url = this.urls['api'] + '/' + this.version + '/';
        if (api === 'private') {
            url += api + '/';
        }
        url += this.implodeParams (path, params);
        const query = this.omit (params, this.extractParams (path));
        if (api === 'public') {
            if (Object.keys (query).length) {
                url += '?' + this.urlencode (query);
            }
        } else {
            this.checkRequiredCredentials ();
            const nonce = this.nonce ().toString ();
            url += '?' + this.urlencode ({ 'nonce': nonce });
            const signature = this.hmac (this.encode (url), this.encode (this.secret), 'sha512', 'base64');
            body = this.urlencode (this.extend ({
                'apikey': this.apiKey,
                'signature': signature,
            }, query));
            headers = {
                'Content-Type': 'application/x-www-form-urlencoded',
            };
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }

    async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        const response = await this.fetch2 (path, api, method, params, headers, body);
        if ('status' in response) {
            if (response['status'] !== 'success') {
                throw new ExchangeError (this.id + ' ' + this.json (response));
            }
        }
        return response;
    }
};
<|endoftext|>


<commit_before><commit_msg>Force year to be string to not break jekyll build<commit_after><|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';

import {
    View,
    Text,
    Navigator,
    StatusBar,
    TouchableHighlight,
    StyleSheet
} from 'react-native';
import Calendar from 'react-native-calendar';
const colors = [
    '#ff0000',
    '#003388',
    '#0000ff',
  ];

  function getValue<T>(values: Array<T>, index: number): T {
  return values[index % values.length];
}
class HomeTab extends Component {

  state = {
    animated: true,
    backgroundColor: getValue(colors, 0),
  };

  _colorIndex = 0;

  _onChangeBackgroundColor = () => {
    this._colorIndex++;
    this.setState({backgroundColor: getValue(colors, this._colorIndex)});
  };

  _onChangeAnimated = () => {
    this.setState({animated: !this.state.animated});
  };
    render(){

      const routes = [
        {title: '30 Day View', index: 0},
        {title: 'Month', index: 1},
      ];
      return (
        <View>
          <StatusBar
            backgroundColor={this.state.backgroundColor}
            animated={this.state.animated}
          />
          <TouchableHighlight
            style={styles.wrapper}
            onPress={this._onChangeBackgroundColor}>
            <View style={styles.button}>
              <Text>backgroundColor: '{getValue(colors, this._colorIndex)}'</Text>
            </View>
          </TouchableHighlight>

          <Navigator
            initialRoute={routes[0]}
            initialRouteStack={routes}
            renderScene={(route, navigator) =>
            <View>
              <StatusBar hidden={route.statusBarHidden} />
              <MonthlyScene
                title= {routes[route.index].title}

                onForward={ () => {
                  const nxtId = route.index + 1;
                  navigator.push(routes[nxtId]);
                }}

                onBack={ () => {
                  if (route.index > 0) {
                    navigator.pop();
                  }
                }}
              >
              </MonthlyScene>
            </View>
          }
          >
          </Navigator>
        </View>
      )
        // return (
        //   <View style={styles.container}>
        //     <StatusBar
        //        backgroundColor="blue"
        //        barStyle="light-content"
        //      />
        //     <Navigator
        //     initialRoute={routes[0]}
        //     initialRouteStack={routes}
        //     renderScene={(route, navigator) =>
        //       <View>
        //         <StatusBar hidden={route.statusBarHidden} />
        //         <MonthlyScene
        //           title= {routes[route.index].title}

        //           onForward={ () => {
        //             const nxtId = route.index + 1;
        //             navigator.push(routes[nxtId]);
        //           }}

        //           onBack={ () => {
        //             if (route.index > 0) {
        //               navigator.pop();
        //             }
        //           }}
        //         >
        //         </MonthlyScene>
        //       </View>
        //     }
        //     >
        //     </Navigator>
        //   </View>
        // )
      }
};

class MonthlyScene extends Component {
  static propTypes = {
    title: PropTypes.string.isRequired,
    onForward: PropTypes.func.isRequired,
    onBack: PropTypes.func.isRequired
  }

  render() {
    return (
      <View>
        <Text>Current Scene: {this.props.title}</Text>
        <TouchableHighlight onPress={this.props.onForward}>
          <Text>Tap me to load the next scene</Text>
        </TouchableHighlight>
        <TouchableHighlight onPress={this.props.onBack}>
          <Text>Tap me to go back</Text>
        </TouchableHighlight>
      </View>
    )
  }
};

class MonthlyScene2 extends Component {
  static propTypes = {
    title: PropTypes.string.isRequired,
    onForward: PropTypes.func.isRequired,
    onBack: PropTypes.func.isRequired
  }

  render() {
    return (
      <View>
        <Text>Current Scene</Text>
        <TouchableHighlight onPress={this.props.onForward}>
          <Text>Tap me to load the next scene</Text>
        </TouchableHighlight>
        <TouchableHighlight onPress={this.props.onBack}>
          <Text>Tap me to go back</Text>
        </TouchableHighlight>
      </View>
    )
  }
};

const styles = StyleSheet.create({
   wrapper: {
    borderRadius: 5,
    marginBottom: 5,
  },
  button: {
    borderRadius: 5,
    backgroundColor: '#eeeeee',
    padding: 10,
  },
  title: {
    marginTop: 16,
    marginBottom: 8,
    fontWeight: 'bold',
  },
  container: {
    flex: 1,
    // justifyContent: 'center',
    // alignItems: 'center',
    backgroundColor: '#F5FCFF',
    marginTop: 20
  },
  calendar: {
    fontSize: 10,
    textAlign: 'center',
    marginTop: 100,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});

module.exports = HomeTab;<commit_msg>Navigator<commit_after>import React, { Component, PropTypes } from 'react';

import {
    View,
    Text,
    Navigator,
    StatusBar,
    TouchableHighlight,
    StyleSheet
} from 'react-native';
import Calendar from 'react-native-calendar';
const colors = [
    '#ff0000',
    '#003388',
    '#0000ff',
  ];

  function getValue<T>(values: Array<T>, index: number): T {
  return values[index % values.length];
}
class HomeTab extends Component {

  state = {
    animated: true,
    backgroundColor: getValue(colors, 0),
  };

  _colorIndex = 0;

  _onChangeBackgroundColor = () => {
    this._colorIndex++;
    this.setState({backgroundColor: getValue(colors, this._colorIndex)});
  };

  _onChangeAnimated = () => {
    this.setState({animated: !this.state.animated});
  };
    render(){

      const routes = [
        {title: '30 Day View', index: 0},
        {title: 'Month', index: 1},
      ];

        return (

            <Navigator
            initialRoute={routes[0]}
            initialRouteStack={routes}
            renderScene={(route, navigator) =>
              <View>

                <MonthlyScene
                  title= {routes[route.index].title}

                  onForward={ () => {
                    const nxtId = route.index + 1;
                    navigator.push(routes[nxtId]);
                  }}

                  onBack={ () => {
                    if (route.index > 0) {
                      navigator.pop();
                    }
                  }}
                >
                </MonthlyScene>
              </View>
            }
            >
            </Navigator>
        )
      }
};

class MonthlyScene extends Component {
  static propTypes = {
    title: PropTypes.string.isRequired,
    onForward: PropTypes.func.isRequired,
    onBack: PropTypes.func.isRequired
  }

  render() {
    return (
      <View>
        <Text>Current Scene: {this.props.title}</Text>
        <TouchableHighlight onPress={this.props.onForward}>
          <Text>Tap me to load the next scene</Text>
        </TouchableHighlight>
        <TouchableHighlight onPress={this.props.onBack}>
          <Text>Tap me to go back</Text>
        </TouchableHighlight>
      </View>
    )
  }
};

class MonthlyScene2 extends Component {
  static propTypes = {
    title: PropTypes.string.isRequired,
    onForward: PropTypes.func.isRequired,
    onBack: PropTypes.func.isRequired
  }

  render() {
    return (
      <View>
        <Text>Current Scene</Text>
        <TouchableHighlight onPress={this.props.onForward}>
          <Text>Tap me to load the next scene</Text>
        </TouchableHighlight>
        <TouchableHighlight onPress={this.props.onBack}>
          <Text>Tap me to go back</Text>
        </TouchableHighlight>
      </View>
    )
  }
};

const styles = StyleSheet.create({
   wrapper: {
    borderRadius: 5,
    marginBottom: 5,
  },
  button: {
    borderRadius: 5,
    backgroundColor: '#eeeeee',
    padding: 10,
  },
  title: {
    marginTop: 16,
    marginBottom: 8,
    fontWeight: 'bold',
  },
  container: {
    flex: 1,
    // justifyContent: 'center',
    // alignItems: 'center',
    backgroundColor: '#F5FCFF',
    marginTop: 20
  },
  calendar: {
    fontSize: 10,
    textAlign: 'center',
    marginTop: 100,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});

module.exports = HomeTab;
<|endoftext|>


<commit_before>function setMeter(rating) {
	if(rating>=6) {
		var x=500;
	} else if(rating<=-6) {
		var x=-500;
	} else {
		var x=rating*100;
	}
	var r=$("#meterRed");
	var g=$("#meterGreen");
	g.animate({width: (583+x)}, Math.abs(g.width()-(583+x)));
	r.animate({width: (583-x)}, Math.abs(r.width()-(583-x)));
}

var first=true;
var player;
var firebase = new Firebase('https://adr2370.firebaseio.com/');
var songdb = firebase.child('songs');
var playerdb = firebase.child('playerdb');
var commentdb = firebase.child('comments');
var currentdb = firebase.child('current');

var songs=new Array();

function nextVideo() {
	setMeter(0);
	$("#comments").html("");
	commentdb.remove();
	if(songs.length>0) {
		songdb.child(songs[0]).once('value', function(dataSnapshot) {
			currentdb.set(dataSnapshot.val());
			currentdb.child('rating').set(0);
			currentdb.child('skip').set(0);
			currentdb.child('play').set(0);
			currentdb.child('pause').set(0);
			currentdb.child('fullScreen').set(0);
			//youtube video
			if(player==null) {
				player=new YT.Player('youtube', {
						height: '390',
						width: '640',
						videoId: songs[0],
				  playerVars: { autoplay:1, enablejsapi:1, modestbranding:1, rel:0, showinfo:0, iv_load_policy:3, volume:50 },
						events: {
							'onStateChange': onPlayerStateChange,
							'onError': onError
						}});
			} else {
				player.loadVideoById(songs[0], 5, "large");
			}
			if(player!=null) {
				playerdb.child('volume').once('value', function(dataSnapshot) {
					player.setVolume(dataSnapshot.val());
				});
			} else {
				console.log("SOMETHING WENT WRONG");
			}
			$("#visuals canvas").hide();
			$("#"+songs[0]).remove();
			songdb.child(songs[0]).remove();
			songs.splice(0,1);
		});
	} else {
		first=true;
		$("#youtube").replaceWith($('
<\/div>'));
		player=null;
	}
}

function onPlayerStateChange(event) {
	if(event.data == 0) {
		nextVideo();
	}
}

function onError(event) {
	nextVideo();
}

currentdb.on('child_changed', function(snapshot, prevChildName) {
	//CHANGE RATING OR SKIP VIDEO
	if(snapshot.name()=="rating") {
		setMeter(snapshot.val());
	} else if(snapshot.name()=="skip"&&snapshot.val()==1) {
		nextVideo();
	} else if(snapshot.name()=="play"&&snapshot.val()==1) {
		currentdb.child('play').set(0);
		player.playVideo();
	} else if(snapshot.name()=="pause"&&snapshot.val()==1) {
		currentdb.child('pause').set(0);
		player.pauseVideo();
	}  else if(snapshot.name()=="fullScreen"&&snapshot.val()==1) {
		currentdb.child('fullScreen').set(0);
		window.fullScreen();
	}
});

songdb.on('child_added', function(snapshot, prevChildName) {
	//ADDS SOMETHING TO QUEUE
	//snapshot.name() is youtube id, all else is below
	songs.push(snapshot.name());
	$("#queue").append('
'+snapshot.child('name').val()+'<\/td>'+snapshot.child('length').val()+'<\/td><\/td>'+snapshot.child('numViews').val()+'<\/tr>');
	if(first) {
		first=false;
		nextVideo();
	}
});

songdb.on('child_moved', function(snapshot, prevChildName) {
	//SWITCHES ORDER OF STUFF IN QUEUE
	//prevChildName is the name of the previous one, snapshot.name is the id of the snapshot
	if(prevChildName==null) {
		$("#topQueue").after($("#"+snapshot.name()).remove());
	} else {
		$("#"+prevChildName).after($("#"+snapshot.name()).remove());
	}
});

playerdb.on('child_changed', function(snapshot, prevChildName) {
	player.setVolume(snapshot.val());
});

commentdb.on('child_added', function(snapshot, prevChildName) {
	//COMMENTS ADDED
	//snapshot.val() is each comment
	$("#comments").prepend("
" + snapshot.val()+"<\/p>");
});<commit_msg>Debugging volume<commit_after>function setMeter(rating) {
	if(rating>=6) {
		var x=500;
	} else if(rating<=-6) {
		var x=-500;
	} else {
		var x=rating*100;
	}
	var r=$("#meterRed");
	var g=$("#meterGreen");
	g.animate({width: (583+x)}, Math.abs(g.width()-(583+x)));
	r.animate({width: (583-x)}, Math.abs(r.width()-(583-x)));
}

var first=true;
var player;
var firebase = new Firebase('https://adr2370.firebaseio.com/');
var songdb = firebase.child('songs');
var playerdb = firebase.child('playerdb');
var commentdb = firebase.child('comments');
var currentdb = firebase.child('current');

var songs=new Array();

function nextVideo() {
	setMeter(0);
	$("#comments").html("");
	commentdb.remove();
	if(songs.length>0) {
		songdb.child(songs[0]).once('value', function(dataSnapshot) {
			currentdb.set(dataSnapshot.val());
			currentdb.child('rating').set(0);
			currentdb.child('skip').set(0);
			currentdb.child('play').set(0);
			currentdb.child('pause').set(0);
			currentdb.child('fullScreen').set(0);
			//youtube video
			if(player==null) {
				player=new YT.Player('youtube', {
						height: '390',
						width: '640',
						videoId: songs[0],
				  playerVars: { autoplay:1, enablejsapi:1, modestbranding:1, rel:0, showinfo:0, iv_load_policy:3, volume:50 },
						events: {
							'onStateChange': onPlayerStateChange,
							'onError': onError
						}});
			} else {
				player.loadVideoById(songs[0], 5, "large");
			}
			/*if(player!=null) {
				playerdb.child('volume').once('value', function(dataSnapshot) {
					player.setVolume(dataSnapshot.val());
				});
			} else {
				console.log("SOMETHING WENT WRONG");
			}*/
			$("#visuals canvas").hide();
			$("#"+songs[0]).remove();
			songdb.child(songs[0]).remove();
			songs.splice(0,1);
		});
	} else {
		first=true;
		$("#youtube").replaceWith($('
<\/div>'));
		player=null;
	}
}

function onPlayerStateChange(event) {
	if(event.data == 0) {
		nextVideo();
	}
}

function onError(event) {
	nextVideo();
}

currentdb.on('child_changed', function(snapshot, prevChildName) {
	//CHANGE RATING OR SKIP VIDEO
	if(snapshot.name()=="rating") {
		setMeter(snapshot.val());
	} else if(snapshot.name()=="skip"&&snapshot.val()==1) {
		nextVideo();
	} else if(snapshot.name()=="play"&&snapshot.val()==1) {
		currentdb.child('play').set(0);
		player.playVideo();
	} else if(snapshot.name()=="pause"&&snapshot.val()==1) {
		currentdb.child('pause').set(0);
		player.pauseVideo();
	}  else if(snapshot.name()=="fullScreen"&&snapshot.val()==1) {
		currentdb.child('fullScreen').set(0);
		window.fullScreen();
	}
});

songdb.on('child_added', function(snapshot, prevChildName) {
	//ADDS SOMETHING TO QUEUE
	//snapshot.name() is youtube id, all else is below
	songs.push(snapshot.name());
	$("#queue").append('
'+snapshot.child('name').val()+'<\/td>'+snapshot.child('length').val()+'<\/td><\/td>'+snapshot.child('numViews').val()+'<\/tr>');
	if(first) {
		first=false;
		nextVideo();
	}
});

songdb.on('child_moved', function(snapshot, prevChildName) {
	//SWITCHES ORDER OF STUFF IN QUEUE
	//prevChildName is the name of the previous one, snapshot.name is the id of the snapshot
	if(prevChildName==null) {
		$("#topQueue").after($("#"+snapshot.name()).remove());
	} else {
		$("#"+prevChildName).after($("#"+snapshot.name()).remove());
	}
});

playerdb.on('child_changed', function(snapshot, prevChildName) {
	player.setVolume(snapshot.val());
});

commentdb.on('child_added', function(snapshot, prevChildName) {
	//COMMENTS ADDED
	//snapshot.val() is each comment
	$("#comments").prepend("
" + snapshot.val()+"<\/p>");
});<|endoftext|>