changeset 383:38439b21d369 Dev_main

STASH: UNSTABLE. Implementing APE multiple sliders.
author Nicholas Jillings <n.g.r.jillings@se14.qmul.ac.uk>
date Tue, 08 Dec 2015 12:01:48 +0000
parents b1cb28c63a19
children b96fc647f383
files ape.css ape.js core.js example_eval/project.xml
diffstat 4 files changed, 174 insertions(+), 138 deletions(-) [+]
line wrap: on
line diff
--- a/ape.css	Mon Dec 07 18:42:36 2015 +0000
+++ b/ape.css	Tue Dec 08 12:01:48 2015 +0000
@@ -14,7 +14,8 @@
 div.pageTitle {
 	width: auto;
 	height: 20px;
-	margin-top: 20px;
+	margin-top: 5px;
+	margin-bottom: 10px;
 }
 
 div.pageTitle span{
@@ -27,7 +28,7 @@
 	background-color: #ddd
 }
 
-div#slider {
+div.slider {
 	/* Specify any structure for the slider holder interface */
 	background-color: #eee;
 	height: 150px;
--- a/ape.js	Mon Dec 07 18:42:36 2015 +0000
+++ b/ape.js	Tue Dec 08 12:01:48 2015 +0000
@@ -147,33 +147,34 @@
 		var audioObjs = audioEngineContext.audioObjects;
 		var audioHolder = testState.stateMap[testState.stateIndex];
 		var interfaces = audioHolder.interfaces;
-		
-		var minRanking = audioObjs[0].interfaceDOM.getValue();
-		var maxRanking = minRanking;
-		
-		var minScale;
-		var maxScale;
-		for (var i=0; i<interfaces[0].options.length; i++)
+		for (var i=0; i<interfaces.length; i++)
 		{
-			if (interfaces[0].options[i].check == "scalerange") {
-				minScale = interfaces[0].options[i].min;
-				maxScale = interfaces[0].options[i].max;
+			var minRanking = convSliderPosToRate(audioObjs[0].interfaceDOM.trackSliderObjects[i]);
+			var maxRanking = minRanking;
+			
+			var minScale;
+			var maxScale;
+			for (var j=0; j<interfaces[i].options.length; j++)
+			{
+				if (interfaces[i].options[j].check == "scalerange") {
+					minScale = interfaces[i].options[j].min;
+					maxScale = interfaces[i].options[j].max;
+					break;
+				}
+			}
+			for (var j=1; j<audioObjs.length; j++){
+				if (audioObjs[j].specification.type != 'outsidereference') {
+					var ranking = convSliderPosToRate(audioObjs[j].interfaceDOM.trackSliderObjects[i]);
+					if (ranking < minRanking) { minRanking = ranking;}
+					if (ranking > maxRanking) { maxRanking = ranking;}
+				}
+			}
+			if (minRanking > minScale || maxRanking < maxScale) {
+				alert('Please use the full width of the scale');
+				return false;
 			}
 		}
-		
-		for (var i=1; i<audioObjs.length; i++){
-			if (audioObjs[i].specification.type != 'outsidereference') {
-				var ranking = audioObjs[i].interfaceDOM.getValue();
-				if (ranking < minRanking) { minRanking = ranking;}
-				if (ranking > maxRanking) { maxRanking = ranking;}
-			}
-		}
-		if (minRanking > minScale || maxRanking < maxScale) {
-			alert('Please use the full width of the scale');
-			return false;
-		} else {
-			return true;
-		}
+		return true;
 	};
 	
 	Interface.prototype.objectSelected = null;
@@ -225,13 +226,6 @@
 	// Insert the titleSpan element into the title div element.
 	title.appendChild(titleSpan);
 	
-	var pagetitle = document.createElement('div');
-	pagetitle.className = "pageTitle";
-	pagetitle.align = "center";
-	var titleSpan = document.createElement('span');
-	titleSpan.id = "pageTitle";
-	pagetitle.appendChild(titleSpan);
-	
 	// Create Interface buttons!
 	var interfaceButtons = document.createElement('div');
 	interfaceButtons.id = 'interface-buttons';
@@ -259,38 +253,9 @@
 	interfaceButtons.appendChild(playback);
 	interfaceButtons.appendChild(submit);
 	
-	// Now create the slider and HTML5 canvas boxes
+	var sliderHolder = document.createElement("div");
+	sliderHolder.id = "slider-holder";
 	
-	// Create the div box to center align
-	var sliderBox = document.createElement('div');
-	sliderBox.className = 'sliderCanvasDiv';
-	sliderBox.id = 'sliderCanvasHolder';
-	
-	// Create the slider box to hold the slider elements
-	var canvas = document.createElement('div');
-	canvas.id = 'slider';
-	canvas.align = "left";
-	canvas.addEventListener('dragover',function(event){
-		event.preventDefault();
-		event.dataTransfer.effectAllowed = 'none';
-		event.dataTransfer.dropEffect = 'copy';
-		return false;
-	},false);
-	var sliderMargin = document.createAttribute('marginsize');
-	sliderMargin.nodeValue = 42; // Set default margins to 42px either side
-	// Must have a known EXACT width, as this is used later to determine the ratings
-	var w = (Number(sliderMargin.nodeValue)+8)*2;
-	canvas.style.width = width - w +"px";
-	canvas.style.marginLeft = sliderMargin.nodeValue +'px';
-	canvas.setAttributeNode(sliderMargin);
-	sliderBox.appendChild(canvas);
-	
-	// Create the div to hold any scale objects
-	var scale = document.createElement('div');
-	scale.className = 'sliderScale';
-	scale.id = 'sliderScaleHolder';
-	scale.align = 'left';
-	sliderBox.appendChild(scale);
 	
 	// Global parent for the comment boxes on the page
 	var feedbackHolder = document.createElement('div');
@@ -301,9 +266,8 @@
 	
 	// Inject into HTML
 	testContent.appendChild(title); // Insert the title
-	testContent.appendChild(pagetitle);
 	testContent.appendChild(interfaceButtons);
-	testContent.appendChild(sliderBox);
+	testContent.appendChild(sliderHolder);
 	testContent.appendChild(feedbackHolder);
 	interfaceContext.insertPoint.appendChild(testContent);
 
@@ -315,16 +279,78 @@
 
 function loadTest(audioHolderObject)
 {
-
+	var width = window.innerWidth;
+	var height = window.innerHeight;
 	var id = audioHolderObject.id;
 	
 	var feedbackHolder = document.getElementById('feedbackHolder');
-	var canvas = document.getElementById('slider');
+	var sliderHolder = document.getElementById('slider-holder');
 	feedbackHolder.innerHTML = null;
-	canvas.innerHTML = null;
+	sliderHolder.innerHTML = null;
 	
 	var interfaceObj = audioHolderObject.interfaces;
 	for (var k=0; k<interfaceObj.length; k++) {
+		// Create the div box to center align
+		var sliderBox = document.createElement('div');
+		sliderBox.className = 'sliderCanvasDiv';
+		sliderBox.id = 'sliderCanvasHolder-'+k;
+		
+		var pagetitle = document.createElement('div');
+		pagetitle.className = "pageTitle";
+		pagetitle.align = "center";
+		var titleSpan = document.createElement('span');
+		titleSpan.id = "pageTitle-"+k;
+		if (interfaceObj[k].title != undefined && typeof interfaceObj[k].title == "string")
+		{
+			titleSpan.textContent = interfaceObj[k].title;
+		}
+		pagetitle.appendChild(titleSpan);
+		sliderBox.appendChild(pagetitle);
+		
+		// Create the slider box to hold the slider elements
+		var canvas = document.createElement('div');
+		if (interfaceObj[k].name != undefined)
+			canvas.id = 'slider-'+name;
+		else
+			canvas.id = 'slider-'+k;
+		canvas.className = 'slider';
+		canvas.align = "left";
+		canvas.addEventListener('dragover',function(event){
+			event.preventDefault();
+			event.dataTransfer.effectAllowed = 'none';
+			event.dataTransfer.dropEffect = 'copy';
+			return false;
+		},false);
+		var sliderMargin = document.createAttribute('marginsize');
+		sliderMargin.nodeValue = 42; // Set default margins to 42px either side
+		// Must have a known EXACT width, as this is used later to determine the ratings
+		var w = (Number(sliderMargin.nodeValue)+8)*2;
+		canvas.style.width = width - w +"px";
+		canvas.style.marginLeft = sliderMargin.nodeValue +'px';
+		canvas.setAttributeNode(sliderMargin);
+		sliderBox.appendChild(canvas);
+		
+		// Create the div to hold any scale objects
+		var scale = document.createElement('div');
+		scale.className = 'sliderScale';
+		scale.id = 'sliderScaleHolder';
+		scale.align = 'left';
+		sliderBox.appendChild(scale);
+		sliderHolder.appendChild(sliderBox);
+		var positionScale = canvas.style.width.substr(0,canvas.style.width.length-2);
+		var offset = Number(canvas.attributes['marginsize'].value);
+		$(interfaceObj[k].scale).each(function(index,scaleObj){
+			var value = document.createAttribute('value');
+			var position = Number(scaleObj[0])*0.01;
+			value.nodeValue = position;
+			var pixelPosition = (position*positionScale)+offset;
+			var scaleDOM = document.createElement('span');
+			scaleDOM.textContent = scaleObj[1];
+			scale.appendChild(scaleDOM);
+			scaleDOM.style.left = Math.floor((pixelPosition-($(scaleDOM).width()/2)))+'px';
+			scaleDOM.setAttributeNode(value);
+		});
+		
 		for (var i=0; i<interfaceObj[k].options.length; i++)
 		{
 			if (interfaceObj[k].options[i].type == 'option' && interfaceObj[k].options[i].name == 'playhead')
@@ -352,36 +378,8 @@
 			}
 		}
 	}
-	// Setup question title
 	
 	var commentBoxPrefix = "Comment on track";
-	if (interfaceObj.length != 0) {
-		interfaceObj = interfaceObj[0];
-		var titleNode = interfaceObj.title;
-		if (titleNode != undefined)
-		{
-			document.getElementById('pageTitle').textContent = titleNode;
-		}
-		var positionScale = canvas.style.width.substr(0,canvas.style.width.length-2);
-		var offset = Number(document.getElementById('slider').attributes['marginsize'].value);
-		var scale = document.getElementById('sliderScaleHolder');
-		scale.innerHTML = null;
-		$(interfaceObj.scale).each(function(index,scaleObj){
-			var value = document.createAttribute('value');
-			var position = Number(scaleObj[0])*0.01;
-			value.nodeValue = position;
-			var pixelPosition = (position*positionScale)+offset;
-			var scaleDOM = document.createElement('span');
-			scaleDOM.textContent = scaleObj[1];
-			scale.appendChild(scaleDOM);
-			scaleDOM.style.left = Math.floor((pixelPosition-($(scaleDOM).width()/2)))+'px';
-			scaleDOM.setAttributeNode(value);
-		});
-		
-		if (interfaceObj.commentBoxPrefix != undefined) {
-			commentBoxPrefix = interfaceObj.commentBoxPrefix;
-		}
-	}
 	
 	var commentShow = audioHolderObject.elementComments;
 	
@@ -403,20 +401,11 @@
 		var node = interfaceContext.createCommentBox(audioObject);
 		
 		// Create a slider per track
-		audioObject.interfaceDOM = new sliderObject(audioObject);
+		audioObject.interfaceDOM = new sliderObject(audioObject,interfaceObj);
 		if (audioObject.state == 1)
 		{
 			audioObject.interfaceDOM.enable();
 		}
-		
-		// Distribute it randomnly
-		var w = window.innerWidth - (offset+8)*2;
-		w = Math.random()*w;
-		w = Math.floor(w+(offset+8));
-		audioObject.interfaceDOM.trackSliderObj.style.left = w+'px';
-		
-		canvas.appendChild(audioObject.interfaceDOM.trackSliderObj);
-		audioObject.metric.initialised(convSliderPosToRate(audioObject.interfaceDOM.trackSliderObj));
         
 	});
 	
@@ -428,7 +417,7 @@
 		event.preventDefault();
 	});
 	
-	$('#slider').mousemove(function(event) {
+	$('.slider').mousemove(function(event) {
 		event.preventDefault();
 		var obj = interfaceContext.getSelectedObject();
 		if (obj == null) {return;}
@@ -454,9 +443,10 @@
 			audioEngineContext.play(id);
 	        // Currently playing track red, rest green
 	        
-	        //document.getElementById('track-slider-'+index).style.backgroundColor = "#FF0000";
+	        
 	        $('.track-slider').removeClass('track-slider-playing');
-	        $(obj).addClass('track-slider-playing');
+	        var name = ".track-slider-"+obj.getAttribute("trackindex");
+	        $(name).addClass('track-slider-playing');
 	        $('.comment-div').removeClass('comment-box-playing');
 	        $('#comment-div-'+id).addClass('comment-box-playing');
 	        var outsideReference = document.getElementById('outside-reference');
@@ -505,30 +495,55 @@
 	//testWaitIndicator();
 }
 
-function sliderObject(audioObject) {
+function sliderObject(audioObject,interfaceObjects) {
 	// Create a new slider object;
 	this.parent = audioObject;
-	this.trackSliderObj = document.createElement('div');
-	this.trackSliderObj.className = 'track-slider track-slider-disabled';
-	this.trackSliderObj.id = 'track-slider-'+audioObject.id;
-
-	this.trackSliderObj.setAttribute('trackIndex',audioObject.id);
-	this.trackSliderObj.innerHTML = '<span>'+audioObject.id+'</span>';
+	this.trackSliderObjects = [];
+	for (var i=0; i<interfaceObjects.length; i++)
+	{
+		var trackObj = document.createElement('div');
+		trackObj.className = 'track-slider track-slider-disabled track-slider-'+audioObject.id;
+		trackObj.id = 'track-slider-'+i+'-'+audioObject.id;
+		trackObj.setAttribute('trackIndex',audioObject.id);
+		trackObj.innerHTML = '<span>'+audioObject.id+'</span>';
+		if (interfaceObjects[i].name != undefined) {
+			trackObj.setAttribute('interface-name',interfaceObjects[i].name);
+		} else {
+			trackObj.setAttribute('interface-name',i);
+		}
+		this.trackSliderObjects.push(trackObj);
+		var slider = document.getElementById("slider-"+trackObj.getAttribute("interface-name"));
+		var offset = Number(slider.attributes['marginsize'].value);
+		// Distribute it randomnly
+		var w = window.innerWidth - (offset+8)*2;
+		w = Math.random()*w;
+		w = Math.floor(w+(offset+8));
+		trackObj.style.left = w+'px';
+		slider.appendChild(trackObj);
+	}
 
 	// Onclick, switch playback to that track
 	
 	this.enable = function() {
 		if (this.parent.state == 1)
 		{
-			$(this.trackSliderObj).removeClass('track-slider-disabled');
+			$(this.trackSliderObjects).each(function(i,trackObj){
+				$(trackObj).removeClass('track-slider-disabled');
+			});
 		}
 	};
 	
 	this.exportXMLDOM = function(audioObject) {
 		// Called by the audioObject holding this element. Must be present
-		var node = document.createElement('value');
-		node.textContent = convSliderPosToRate(this.trackSliderObj);
-		return node;
+		var obj = [];
+		$(this.trackSliderObjects).each(function(i,trackObj){
+			var node = document.createElement('value');
+			node.setAttribute("name",trackObj.getAttribute("interface-name"));
+			node.textContent = convSliderPosToRate(trackObj);
+			obj.push(node);
+		});
+		
+		return obj;
 	};
 	this.getValue = function() {
 		return convSliderPosToRate(this.trackSliderObj);
@@ -596,12 +611,13 @@
     } 
 }
 
-function convSliderPosToRate(slider)
+function convSliderPosToRate(trackSlider)
 {
-	var w = document.getElementById('slider').style.width;
-	var marginsize = Number(document.getElementById('slider').attributes['marginsize'].value);
+	var slider = trackSlider.parentElement;
+	var w = slider.style.width;
+	var marginsize = Number(slider.attributes['marginsize'].value);
 	var maxPix = w.substr(0,w.length-2);
-	var pix = slider.style.left;
+	var pix = trackSlider.style.left;
 	pix = pix.substr(0,pix.length-2);
 	var rate = (pix-marginsize)/maxPix;
 	return rate;
--- a/core.js	Mon Dec 07 18:42:36 2015 +0000
+++ b/core.js	Tue Dec 08 12:01:48 2015 +0000
@@ -649,7 +649,6 @@
 	
 	this.testPageCompleted = function(store, testXML, testId) {
 		// Function called each time a test page has been completed
-		// Can be used to over-rule default behaviour
 		var metric = document.createElement('metric');
 		if (audioEngineContext.metric.enableTestTimer)
 		{
@@ -658,17 +657,17 @@
 			testTime.textContent = audioEngineContext.timer.testDuration;
 			metric.appendChild(testTime);
 		}
-		testXML.appendChild(metric);
+		store.appendChild(metric);
 		var audioObjects = audioEngineContext.audioObjects;
 		for (var i=0; i<audioObjects.length; i++) 
 		{
 			var audioElement = audioEngineContext.audioObjects[i].exportXMLDOM();
 			audioElement.setAttribute('presentedId',i);
-			testXML.appendChild(audioElement);
+			store.appendChild(audioElement);
 		}
 		$(interfaceContext.commentQuestions).each(function(index,element){
 			var node = element.exportXMLDOM();
-			testXML.appendChild(node);
+			store.appendChild(node);
 		});
 		pageXMLSave(store, testXML);
 	};
@@ -914,10 +913,10 @@
 		var maxId;
 		for (var i=0; i<this.audioObjects.length; i++)
 		{
-			lens.push(this.audioObjects[i].buffer.length);
-			if (length < this.audioObjects[i].buffer.length)
+			lens.push(this.audioObjects[i].buffer.buffer.length);
+			if (length < this.audioObjects[i].buffer.buffer.length)
 			{
-				length = this.audioObjects[i].buffer.length;
+				length = this.audioObjects[i].buffer.buffer.length;
 				maxId = i;
 			}
 		}
@@ -929,7 +928,7 @@
 		// Extract the audio and zero-pad
 		for (var i=0; i<lens.length; i++)
 		{
-			var orig = this.audioObjects[i].buffer;
+			var orig = this.audioObjects[i].buffer.buffer;
 			var hold = audioContext.createBuffer(orig.numberOfChannels,length,orig.sampleRate);
 			for (var c=0; c<orig.numberOfChannels; c++)
 			{
@@ -938,7 +937,7 @@
 				for (var n=0; n<orig.length; n++)
 				{inData[n] = outData[n];}
 			}
-			this.audioObjects[i].buffer = hold;
+			this.audioObjects[i].buffer.buffer = hold;
 			delete orig;
 		}
 	};
@@ -1077,7 +1076,15 @@
 		file.setAttribute('duration',this.buffer.duration);
 		root.appendChild(file);
 		if (this.specification.type != 'outsidereference') {
-			root.appendChild(this.interfaceDOM.exportXMLDOM(this));
+			var interfaceXML = this.interfaceDOM.exportXMLDOM(this);
+			if (interfaceXML.length == undefined) {
+				root.appendChild();
+			} else {
+				for (var i=0; i<interfaceXML.length; i++)
+				{
+					root.appendChild(interfaceXML[i]);
+				}
+			}
 			root.appendChild(this.commentDOM.exportXMLDOM(this));
 			if(this.specification.type == 'anchor') {
 				root.setAttribute('anchor',true);
@@ -1503,6 +1510,7 @@
 		}
 		
 		this.commonInterface = new function() {
+			this.name = undefined;
 			this.OptionNode = function(child) {
 				this.type = child.nodeName;
 				if (this.type == 'option')
@@ -1536,6 +1544,10 @@
 			};
 			this.options = [];
 			if (commonInterfaceNode != undefined) {
+				var name = commonInterfaceNode.getAttribute("name");
+				if (name != undefined) {
+					this.name = name;
+				}
 				var child = commonInterfaceNode.firstElementChild;
 				while (child != undefined) {
 					this.options.push(new this.OptionNode(child));
--- a/example_eval/project.xml	Mon Dec 07 18:42:36 2015 +0000
+++ b/example_eval/project.xml	Tue Dec 08 12:01:48 2015 +0000
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <BrowserEvalProjectDocument>
-	<setup interface="APE" projectReturn="save.php" randomiseOrder='true' collectMetrics='true' testPages="2">
+	<setup interface="APE" projectReturn="save.php" randomiseOrder='false' collectMetrics='true' testPages="2">
 		<PreTest>
 			<question id="Location" mandatory="true" boxsize="large">Please enter your location.</question>
 			<checkbox id="experience">
@@ -42,14 +42,21 @@
 		</interface>
 	</setup>
 	<audioHolder id='test-0' hostURL="example_eval/" sampleRate="44100" randomiseOrder='true' repeatCount='0' loop='true' elementComments='true'>
-		<interface>
-			<title>Example Test Question</title>
+		<interface name="preference">
+			<title>Preference</title>
 			<scale position="0">Min</scale>
 			<scale position="100">Max</scale>
 			<scale position="50">Middle</scale>
 			<scale position="20">20</scale>
 			<commentBoxPrefix>Comment on fragment</commentBoxPrefix>
 		</interface>
+		<interface name="depth">
+			<title>Depth</title>
+			<scale position="0">Low</scale>
+			<scale position="100">High</scale>
+			<scale position="50">Middle</scale>
+			<commentBoxPrefix>Comment on fragment</commentBoxPrefix>
+		</interface>
 		<audioElements url="0.wav" id="0" type="anchor"/>
 		<audioElements url="1.wav" id="1"/>
 		<audioElements url="2.wav" id="2"/>