visrecall/WebInterface/Front-end/assets/js/main.js

500 lines
No EOL
16 KiB
JavaScript
Executable file

var qa_counter = 1;
var reco_flag = 0;
var config = {};
var state = {
taskIndex: gup("skipto") ? parseInt(gup("skipto")) : 0,
taskInputs: {},
taskOutputs: [],
//assignmentId: gup("assignmentId"),
assignmentId: Math.floor(Math.random() * 1000),
//workerId: gup("workerId"),
workerId: Math.floor(Math.random() * 1000),
hitId: gup("hitId")
};
// Debug log
var bugout = new debugout();
var rectBugout = new debugout();
/* HELPERS */
function saveTaskData() {
var data;
if (isDemoSurvey()) {
data = demoSurvey.collectData();
} else {
data = custom.collectData(getTaskInputs(state.taskIndex), state.taskIndex, getTaskOutputs(state.taskIndex));
}
if (config.meta.aggregate) {
$.extend(state.taskOutputs, data);
} else {
state.taskOutputs[state.taskIndex] = data;
}
}
function getTaskInputs(i) {
return config.meta.aggregate ? state.taskInputs : state.taskInputs[i];
}
``
function getTaskOutputs(i) {
return config.meta.aggregate ? state.taskOutputs : state.taskOutputs[i];
}
function updateTask() {
//console.log(state.taskIndex);
if (config.advanced.hideIfNotAccepted && hideIfNotAccepted()) {
return;
}
$("#progress-bar").progress("set progress", state.taskIndex + 1);
if (isDemoSurvey()) {
demoSurvey.showTask();
} else {
// show the user's task
demoSurvey.hideSurvey();
$('#custom-experiment').show();
custom.showTask(getTaskInputs(state.taskIndex), state.taskIndex, getTaskOutputs(state.taskIndex));
}
if (state.taskIndex == config.meta.numSubtasks + config.advanced.includeDemographicSurvey - 1) {
// last page
$("#next-button").hide();
$("#qa-button").hide();
$('#nq-button').hide();
$("#reco-button").hide();
if (state.taskIndex != 0) {
$("#prev-button").removeClass("disabled");
} else {
$("#prev-button").addClass("disabled");
}
$("#submit-button").removeClass("disabled");
$("#disclaimer").show();
$("#final-task-fields").css("display", "block"); // added this to custom.js only on the last page (last subtask) of the last task
// NOTE: comments in the above 2 lines only refer to the case where demographic survey is not shown
} else if (state.taskIndex == 0) {
// first page
$("#next-button").removeClass("disabled");
$("#prev-button").addClass("disabled");
$("#submit-button").addClass("disabled");
$("#final-task-fields").css("display", "none");
$("#disclaimer").hide();
} else {
// intermediate page
$("#next-button").removeClass("disabled");
$("#prev-button").removeClass("disabled");
$("#submit-button").addClass("disabled");
$("#final-task-fields").css("display", "none");
$("#disclaimer").hide();
}
}
function nextTask() {
spanPosition();
dd = new Date();
bugout.log('next button clicked for task:' + state.taskIndex + ', at: ' + dd);
bugout.log(dd.getTime());
console.log("moving to next task");
if (qa_counter == 5) {
custom.updateAnswers(qa_counter);
qa_counter = 1;
}
if (state.taskIndex == 79) {
custom.recoAnswers();
}
if (state.taskIndex < (config.meta.numSubtasks + config.advanced.includeDemographicSurvey) - 1) {
saveTaskData();
var failedValidation;
if (isDemoSurvey()) {
failedValidation = demoSurvey.validateTask();
} else {
failedValidation = custom.validateTask(getTaskInputs(state.taskIndex), state.taskIndex, getTaskOutputs(state.taskIndex));
}
if (failedValidation == false) {
state.taskIndex++;
updateTask();
clearMessage();
console.log("Current collected data", state.taskOutputs);
} else {
generateMessage("negative", failedValidation.errorMessage);
}
}
}
function prevTask() {
if (state.taskIndex > 0) {
saveTaskData();
state.taskIndex--;
updateTask();
}
}
function toggleInstructions() {
dd = new Date();
bugout.log('Entering recall stage: ' + dd);
bugout.log(dd.getTime());
rectBugout.log("id x y width height top right bottom left");
if ($("#experiment").css("display") == "none") {
$("#experiment").css("display", "flex");
$("#instructions").css("display", "none");
$("#disclaimer").hide();
updateTask();
} else {
saveTaskData();
$("#experiment").css("display", "none");
$("#instructions").css("display", "flex");
$("#disclaimer").show();
}
}
function clearMessage() {
$("#message-field").html("");
}
function generateMessage(cls, header) {
clearMessage();
if (!header) return;
var messageStr = "<div class='ui message " + cls + "'>";
messageStr += "<i class='close icon'></i>";
messageStr += "<div class='header'>" + header + "</div></div>";
var newMessage = $(messageStr);
$("#message-field").append(newMessage);
newMessage.click(function() {
$(this).closest(".message").transition("fade");
});
}
function addHiddenField(form, name, value) {
// form is a jQuery object, name and value are strings
var input = $("<input type='hidden' name='" + name + "' value=''>");
input.val(value);
form.append(input);
}
function submitHIT() {
console.log("submitting");
$("#copy-key-button").click(function() {
selectText('submit-code');
});
saveTaskData();
clearMessage();
$("#submit-button").addClass("loading");
for (var i = 0; i < config.meta.numSubtasks; i++) {
var failedValidation = custom.validateTask(getTaskInputs(i), i, getTaskOutputs(i));
if (failedValidation) {
cancelSubmit(failedValidation.errorMessage);
return;
}
}
if (config.advanced.includeDemographicSurvey) {
var failedValidation = demoSurvey.validateTask();
if (failedValidation) {
cancelSubmit(failedValidation.errorMessage);
return;
}
}
var results = custom.getUploadPayload(state.taskOutputs);
var payload = {
'assignmentId': state.assignmentId,
'workerId': state.workerId,
'hitId': state.hitId,
//'tag': gup('tag'),
'origin': state.origin,
'results': results
}
var submitUrl;
if (config.advanced.externalSubmit) {
submitUrl = config.advanced.externalSubmitUrl;
externalSubmit(submitUrl, payload);
} else {
submitUrl = decodeURIComponent(gup("turkSubmitTo")) + "/mturk/externalSubmit";
mturkSubmit(submitUrl, payload);
}
}
function cancelSubmit(err) {
console.log("cancelling submit");
$("#submit-button").removeClass("loading");
generateMessage("negative", err);
}
function gup(name) {
var regexS = "[\\?&]" + name + "=([^&#]*)";
var regex = new RegExp(regexS);
var tmpURL = window.location.href;
var results = regex.exec(tmpURL);
if (results == null) return "";
else return results[1];
}
/* SETUP FUNCTIONS */
function populateMetadata(config) {
$(".meta-title").html(config.meta.title);
$(".meta-desc").html(config.meta.description);
$(".instructions-simple").html(config.instructions.simple);
for (var i = 0; i < config.instructions.steps.length; i++) {
$(".instructions-steps").append($("<li>" + config.instructions.steps[i] + "</li>"));
}
$(".disclaimer-text").html(config.meta.disclaimer);
if (config.instructions.images.length > 0) {
$("#sample-task").css("display", "block");
var instructionsIndex = Math.floor(Math.random() * config.instructions.images.length);
var imgEle = "<img class='instructions-img' src='";
imgEle += config.instructions.images[instructionsIndex] + "'></img>";
$("#instructions-demo").append($(imgEle));
}
$("#progress-bar").progress({
total: config.meta.numSubtasks + config.advanced.includeDemographicSurvey,
});
}
function setupButtons() {
$("#next-button").click(nextTask);
$("#prev-button").click(prevTask);
$(".instruction-button").click(toggleInstructions);
$("#submit-button").click(submitHIT);
$("#qa-button").click(qaSubmit);
$("#nq-button").click(qaSubmit);
$("#reco-button").click(recognition_stage);
if (state.assignmentId == "ASSIGNMENT_ID_NOT_AVAILABLE") {
$("#submit-button").remove();
}
}
function recognition_stage() {
spanPosition();
dd = new Date();
bugout.log('Entering recognition stage: ' + dd);
bugout.log(dd.getTime());
if (qa_counter == 5) {
custom.updateAnswers(qa_counter);
qa_counter = 1;
}
$('#question-answer-subtask').hide();
$('#show-image-subtask').hide();
$('#reco-subtask').show();
$('#qa-button').hide();
$('#next-button').show();
$('#nq-button').hide();
$("#reco-button").hide();
}
function updateQA() {
custom.updateAnswers(qa_counter)
$('#question').html(state.taskInputs[state.taskIndex].QA['Q' + qa_counter].question);
$('label[for=A]').html(state.taskInputs[state.taskIndex].QA['Q' + qa_counter].A);
$('label[for=B]').html(state.taskInputs[state.taskIndex].QA['Q' + qa_counter].B);
$('label[for=C]').html(state.taskInputs[state.taskIndex].QA['Q' + qa_counter].C);
}
function spanPosition() {
// Span postion of answer
var allQuestion = document.getElementById("question");
var span = allQuestion.getElementsByTagName("span");
var qSkip = true;
for (j of span) {
var rectQuestion = j.getBoundingClientRect();
var rectQ = j.id + " ";
for (var key in rectQuestion) {
var item = rectQuestion[key];
if (!isNaN(item) && item != 0) {
rectQ = rectQ + item.toString() + " ";
} else if (item == 0) {
qSkip = false;
break;
}
}
// Skip recording data when the "next" button is clicked during encoding interface
if (qSkip) {
rectBugout.log(rectQ);
}
}
// Span position of answer
var allSelection = document.getElementsByClassName("selection");
aSkip = true;
for (i of allSelection) {
var span = i.getElementsByTagName("span");
for (j of span) {
var rectAnswer = j.getBoundingClientRect();
var rectA = j.id + " ";
for (var key in rectAnswer) {
var item = rectAnswer[key];
if (!isNaN(item) && item != 0) {
rectA = rectA + item.toString() + " ";
} else if (item == 0) {
aSkip = false;
break;
}
}
// Skip recording data when the "next" button is clicked during encoding interface
if (aSkip) {
rectBugout.log(rectA);
}
}
}
}
function qaSubmit() {
spanPosition();
dd = new Date();
bugout.log('QA button clicked, question No' + qa_counter + ': ' + dd);
bugout.log(dd.getTime());
if (state.taskIndex < TARGET_NUM && state.taskIndex % 4 > 1) {
if (qa_counter <= 5) {
qa_counter = qa_counter + 1;
updateQA()
$('#question-answer-subtask').show();
$('#qa-button').hide();
$("#reco-button").hide();
if (qa_counter === 5) {
if (state.taskIndex === TARGET_NUM - 1) {
$('#next-button').hide();
$('#nq-button').hide();
$('#reco-button').show();
} else {
$('#next-button').show();
$('#nq-button').hide();
}
} else {
$('#next-button').hide();
$('#nq-button').show();
}
$('#show-image-subtask').show();
}
} else {
nextTask()
}
$('#remembered-char-subtask').hide();
}
/* USEFUL HELPERS */
function isDemoSurvey() {
var useSurvey = config.advanced.includeDemographicSurvey;
var lastTask = state.taskIndex == config.meta.numSubtasks + config.advanced.includeDemographicSurvey - 1;
return useSurvey && lastTask;
}
// Hides the task UI if the user is working within an MTurk iframe and has not accepted the task
// Returns true if the task was hidden, false otherwise
function hideIfNotAccepted() {
if (state.assignmentId == "ASSIGNMENT_ID_NOT_AVAILABLE") {
console.log("Hiding if not accepted");
$('#experiment').hide();
$("#hit-not-accepted").show();
return true;
}
return false;
}
// Code to show the user's validation code; only used if task is configured as an external link
function showSubmitKey(key) {
$('#submit-code').text(key);
$('#experiment').hide();
$('#succesful-submit').show();
selectText('submit-code');
}
// highlights/selects text within an html element
// copied from:
// https://stackoverflow.com/questions/985272/selecting-text-in-an-element-akin-to-highlighting-with-your-mouse
function selectText(node) {
node = document.getElementById(node);
if (document.body.createTextRange) {
const range = document.body.createTextRange();
range.moveToElementText(node);
range.select();
document.execCommand("copy");
} else if (window.getSelection) {
const selection = window.getSelection();
const range = document.createRange();
range.selectNodeContents(node);
selection.removeAllRanges();
selection.addRange(range);
document.execCommand("copy");
} else {
console.warn("Could not select text in node: Unsupported browser.");
}
}
/* SUBMIT FUNCTIONS */
// submit to MTurk as a back-end. MTurk only accepts form submissions and frowns
// upon async POSTs.
function mturkSubmit(submitUrl, results) {
var form = $("#submit-form");
addHiddenField(form, 'assignmentId', state.assignmentId);
addHiddenField(form, 'workerId', state.workerId);
addHiddenField(form, 'results', JSON.stringify(results));
addHiddenField(form, 'feedback', $("#feedback-input").val());
$("#submit-form").attr("action", submitUrl);
$("#submit-form").attr("method", "POST");
// if (DEBUG) {
// return;
// }
$("#submit-form").submit();
$("#submit-button").removeClass("loading");
generateMessage("positive", "Thanks! Your task was submitted successfully.");
$("#submit-button").addClass("disabled");
}
// submit to a customized back-end.
function externalSubmit(submitUrl, results) {
dd = new Date();
bugout.log('Submitting study:' + dd);
bugout.log(dd.getTime());
bugout.log(results);
console.log("payload", results);
console.log("submitUrl", submitUrl);
bugout.downloadLog();
rectBugout.downloadLog();
$.ajax({
url: submitUrl,
type: 'POST',
data: JSON.stringify(results),
dataType: 'json'
}).then(function(response) {
showSubmitKey(response['key']);
}).catch(function(error) {
// This means there was an error connecting to the DEVELOPER'S
// data collection server.
// even if there is a bug/connection problem at this point,
// we want people to be paid.
// use a consistent prefix so we can pick out problem cases,
// and include their worker id so we can figure out what happened
console.log("ERROR", error);
key = "mturk_key_" + state.workerId + "_" + state.assignmentId;
showSubmitKey(key);
})
}
/* MAIN */
$(document).ready(function() {
$.getJSON("config.json").done(function(data) {
config = data;
config.meta.aggregate = true;
state.taskOutputs = {};
custom.loadTasks().done(function(taskInputData) {
config.meta.numSubtasks = taskInputData[1];
state.taskInputs = taskInputData[0];
populateMetadata(config);
demoSurvey.maybeLoadSurvey(config);
setupButtons(config);
});
});
});