babe-project
Advanced tools
Comparing version 0.0.14 to 0.0.15
@@ -1,1 +0,1 @@ | ||
const errors={contactEmail:`There is no contact_email given. Please give a contact_email to the babeInit function,\n\nfor example:\n\nbabeInit({\n ...\n deploy: {\n ...\n contact_email: 'yourcontactemail@email.sample',\n ...\n },\n ...\n});`,prolificURL:`There is no prolificURL given. Please give a prolificURL to the babeInit function,\n\nfor example:\n\nbabeInit({\n ...\n deploy: {\n ...\n prolificURL: 'https://app.prolific.ac/submissions/complete?cc=SAMPLE',\n ...\n },\n ...\n});`,noTrials:`No trials given. Each _babe view takes an object with an obligatory 'trial' property.\n\nfor example:\n\nvar introView = intro({\n ...\n trials: 1,\n ...\n});\n\nYou can find more information at https://github.com/babe-project/babe-base#views-in-_babe`,noName:`No name given. Each _babe view takes an object with an obligatory 'name' property\n\nfor example:\n\nvar introView = intro({\n ...\n name: 'introView',\n ...\n});\n\nYou can find more information at https://github.com/babe-project/babe-base#views-in-_babe`,noData:`No data given. Each _babe view takes an object with an obligatory 'data' property\n\nfor example:\n\nvar mainTrials = forcedChoice({\n ...\n data: my_main_trials,\n ...\n});\n\nThe data is a list of objects defined in your local js file.\n\n_babe's trial views expect each trial object to have specific properties. Here is an example of a forcedCoice view trial:\n\n{\n question: 'How are you today?',\n option1: 'fine',\n option2: 'good'\n}\n\nYou can find more information at https://github.com/babe-project/babe-base#views-in-_babe`,noTrialType:`No trial_type given. Each _babe view takes an object with an obligatory 'trial_type' property\n\nfor example:\n\nvar mainTrials = forcedChoice({\n ...\n trial_type: 'main trials',\n ...\n});\n\nThe trial type is needed for recording the results of your experiment.\n\nYou can find more information at https://github.com/babe-project/babe-base#views-in-_babe`,notAnArray:`The data is not an array. Trial views get an array of objects.\n\nfor example:\n\nvar mainTrials = forcedChoice({\n ...\n data: [\n {\n prop: val,\n prop: val\n },\n {\n prop: val,\n prop:val\n }\n ],\n ...\n});`,noSuchViewName:`The view name listed in progress_bar.in does not exist. Use the view names to reference the views in progress_bar.in.\n\nfor example:\n\nvar mainView = forcedChoice({\n ...\n name: 'myMainView',\n ...\n});\n\nvar introView = intro({\n ...\n name: 'intro',\n ...\n});\n\nbabeInit({\n ...\n progress_bar: {\n in: [\n "myMainView"\n ],\n style: "chunks"\n width: 100\n },\n ...\n});\n`};const babeInit=function(config){const babe={};babe.views_seq=_.flatten(config.views_seq);babe.currentViewCounter=0;babe.currentTrialCounter=0;babe.currentTrialInViewCounter=0;babe.progress_bar=config.progress_bar;babe.global_data={startDate:Date(),startTime:Date.now()};babe.trial_data=[];babe.deploy=config.deploy;babe.deploy.MTurk_server=babe.deploy.deployMethod=="MTurkSandbox"?"https://workersandbox.mturk.com/mturk/externalSubmit":babe.deploy.deployMethod=="MTurk"?"https://www.mturk.com/mturk/externalSubmit":"";babe.deploy.liveExperiment=babe.deploy.deployMethod!=="debug";babe.deploy.is_MTurk=babe.deploy.MTurk_server!=="";babe.deploy.submissionURL=babe.deploy.deployMethod=="localServer"?"http://localhost:4000/api/submit_experiment/"+babe.deploy.experimentID:babe.deploy.serverAppURL+babe.deploy.experimentID;const regex="/submit_experiment/";babe.deploy.checkExperimentURL=babe.deploy.submissionURL.replace(regex,"/check_experiment/");babe.progress=babeProgress(babe);babe.submission=babeSubmit(babe);babe.findNextView=function(){let currentView=babe.views_seq[babe.currentViewCounter];if(babe.currentTrialInViewCounter<currentView.trials){currentView.render(currentView.CT,babe)}else{babe.currentViewCounter++;currentView=babe.views_seq[babe.currentViewCounter];babe.currentTrialInViewCounter=0;if(currentView!==undefined){currentView.render(currentView.CT,babe)}else{$("#main").html(Mustache.render(`<div class='view'>\n <h1 class="title">Nothing more to show</h1>\n </div>`));return}}babe.currentTrialInViewCounter++;babe.currentTrialCounter++;currentView.CT++;if(currentView.hasProgressBar){babe.progress.update()}};(function(){if(babe.deploy.deployMethod==="MTurk"||babe.deploy.deployMethod==="MTurkSandbox"){console.info(`The experiment runs on MTurk (or MTurk's sandbox)\n----------------------------\n\nThe ID of your experiment is ${babe.deploy.experimentID}\n\nThe results will be submitted ${babe.deploy.submissionURL}\n\nand\n\nMTurk's server: ${babe.deploy.MTurk_server}`)}else if(babe.deploy.deployMethod==="Prolific"){console.info(`The experiment runs on Prolific\n-------------------------------\n\nThe ID of your experiment is ${babe.deploy.experimentID}\n\nThe results will be submitted to ${babe.deploy.submissionURL}\n\nwith\n\nProlific URL (must be the same as in the website): ${babe.deploy.prolificURL}`)}else if(babe.deploy.deployMethod==="directLink"){console.info(`The experiment uses Direct Link\n-------------------------------\n\nThe ID of your experiment is ${babe.deploy.experimentID}\n\nThe results will be submitted to ${babe.deploy.submissionURL}`)}else if(babe.deploy.deployMethod==="debug"){console.info(`The experiment is in Debug Mode\n-------------------------------\n\nThe results will be displayed in a table at the end of the experiment and available to download in CSV format.`)}else if(babe.deploy.deployMethod!=="localServer"){throw new Error(`There is no such deployMethod.\n\nPlease use 'debug', 'directLink', 'Mturk', 'MTurkSandbox', 'localServer' or 'Prolific'.\n\nThe deploy method you provided is '${babe.deploy.deployMethod}'.\n\nYou can find more information at https://github.com/babe-project/babe-base`)}if(babe.deploy.deployMethod==="Prolific"&&(babe.deploy.prolificURL===undefined||babe.deploy.prolificURL==="")){throw new Error(errors.prolificURL)}if(babe.deploy.contact_email===undefined||babe.deploy.contact_email===""){throw new Error(errors.contactEmail)}})();if(babe.deploy.deployMethod!=="debug"){$.ajax({type:"GET",url:babe.deploy.checkExperimentURL,crossDomain:true,success:function(responseData,textStatus,jqXHR){babe.progress.add();babe.findNextView()},error:function(jqXHR,textStatus,error){console.log(babe.deploy.checkExperimentURL);alert(`Sorry, there is an error communicating with our server and the experiment cannot proceed. Please return the HIT immediately and contact the author at ${babe.deploy.contact_email}. Please include the following error message: "${jqXHR.responseText}". Thank you for your understanding.`)}})}else{babe.progress.add();babe.findNextView()}};const babeProgress=function(babe){let totalProgressParts=0;let progressTrials=0;let totalProgressChunks=0;let filledChunks=0;let fillChunk=false;const progress={add:function(){babe.views_seq.map(view=>{for(let j=0;j<babe.progress_bar.in.length;j++){if(view.name===babe.progress_bar.in[j]){totalProgressChunks++;totalProgressParts+=view.trials;view.hasProgressBar=true}}})},update:function(){try{addToDOM()}catch(e){console.error(e.message)}const progressBars=$(".progress-bar");let div,filledPart;if(babe.progress_bar.style==="default"){div=$(".progress-bar").width()/totalProgressParts;filledPart=progressTrials*div}else{div=$(".progress-bar").width()/babe.views_seq[babe.currentViewCounter].trials;filledPart=(babe.currentTrialInViewCounter-1)*div}const filledElem=jQuery("<span/>",{id:"filled"}).appendTo(progressBars[filledChunks]);$("#filled").css("width",filledPart);progressTrials++;if(babe.progress_bar.style==="chunks"){if(fillChunk===true){filledChunks++;fillChunk=false}if(filledElem.width()===$(".progress-bar").width()-div){fillChunk=true}for(var i=0;i<filledChunks;i++){progressBars[i].style.backgroundColor="#5187BA"}}}};const addToDOM=function(){var bar;var i;var view=$(".view");var barWidth=babe.progress_bar.width;var clearfix=jQuery("<div/>",{class:"clearfix"});var container=jQuery("<div/>",{class:"progress-bar-container"});view.css("padding-top",30);view.prepend(clearfix);view.prepend(container);if(babe.progress_bar.style==="chunks"){for(i=0;i<totalProgressChunks;i++){bar=jQuery("<div/>",{class:"progress-bar"});bar.css("width",barWidth);container.append(bar)}}else if(babe.progress_bar.style==="separate"){bar=jQuery("<div/>",{class:"progress-bar"});bar.css("width",barWidth);container.append(bar)}else if(babe.progress_bar.style==="default"){bar=jQuery("<div/>",{class:"progress-bar"});bar.css("width",barWidth);container.append(bar)}else{throw new Error('Progress_bar.style can be set to "default", "separate" or "chunks" in experiment.js')}};return progress};function babeSubmit(babe){const submit={submit:function(babe){let data={experiment_id:babe.deploy.experimentID,trials:addEmptyColumns(babe.trial_data)};data=_.merge(babe.global_data,data);if(babe.deploy.is_MTurk){try{const HITData=getHITData();data["assignment_id"]=HITData["assignmentId"];data["worker_id"]=HITData["workerId"];data["hit_id"]=HITData["hitId"];var form=jQuery("<form/>",{id:"mturk-submission-form",action:config_deploy.MTurk_server}).appendTo(".babe-thanks-view");jQuery("<input/>",{type:"hidden",name:"data",value:JSON.stringify(data)}).appendTo(form);jQuery("<input/>",{type:"hidden",name:"assignmentId",value:HITData["assignmentId"]}).appendTo(form)}catch(e){console.error(e)}}if(babe.deploy.liveExperiment){console.log("submits");submitResults(babe.deploy.contact_email,babe.deploy.submissionURL,flattenData(data),babe.deploy)}else{const flattenedData=flattenData(data);$("#warning-message").addClass("babe-nodisplay");jQuery("<h3/>",{text:"Debug Mode"}).appendTo($("#babe-debug-table-container"));jQuery("<div/>",{class:"babe-debug-results",html:formatDebugData(flattenedData)}).appendTo($("#babe-debug-table-container"));createCSVForDownload(flattenedData)}}};function submitResults(contactEmail,submissionURL,data,config){contactEmail=typeof contactEmail!=="undefined"?contactEmail:"not provided";$.ajax({type:"POST",url:submissionURL,crossDomain:true,contentType:"application/json",data:JSON.stringify(data),success:function(responseData,textStatus,jqXHR){console.log(textStatus);$("#warning-message").addClass("babe-nodisplay");$("#thanks-message").removeClass("babe-nodisplay");$("#extra-message").removeClass("babe-nodisplay");if(config.is_MTurk){setTimeout(function(){submitToMTurk(data),500})}},error:function(responseData,textStatus,errorThrown){if(config.is_MTurk){submitToMTurk(data);$("#thanks-message").removeClass("babe-nodisplay")}else{if(textStatus=="timeout"){alert("Oops, the submission timed out. Please try again. If the problem persists, please contact "+contactEmail+", including your ID")}else{alert("Oops, the submission failed. The server says: "+responseData.responseText+"\nPlease try again. If the problem persists, please contact "+contactEmail+"with this error message, including your ID")}}}})}const submitToMTurk=function(){var form=$("#mturk-submission-form");form.submit()};const addEmptyColumns=function(trialData){var columns=[];for(var i=0;i<trialData.length;i++){for(var prop in trialData[i]){if(trialData[i].hasOwnProperty(prop)&&columns.indexOf(prop)===-1){columns.push(prop)}}}for(var j=0;j<trialData.length;j++){for(var k=0;k<columns.length;k++){if(!trialData[j].hasOwnProperty(columns[k])){trialData[j][columns[k]]="NA"}}}return trialData};const formatDebugData=function(flattenedData){var output="<table id='babe-debug-table'>";var t=flattenedData[0];output+="<thead><tr>";for(var key in t){if(t.hasOwnProperty(key)){output+="<th>"+key+"</th>"}}output+="</tr></thead>";output+="<tbody><tr>";var entry="";for(var i=0;i<flattenedData.length;i++){var currentTrial=flattenedData[i];for(var k in t){if(currentTrial.hasOwnProperty(k)){entry=String(currentTrial[k]);output+="<td>"+entry.replace(/ /g," ")+"</td>"}}output+="</tr>"}output+="</tbody></table>";return output};const createCSVForDownload=function(flattenedData){var csvOutput="";var t=flattenedData[0];for(var key in t){if(t.hasOwnProperty(key)){csvOutput+='"'+String(key)+'",'}}csvOutput+="\n";for(var i=0;i<flattenedData.length;i++){var currentTrial=flattenedData[i];for(var k in t){if(currentTrial.hasOwnProperty(k)){csvOutput+='"'+String(currentTrial[k])+'",'}}csvOutput+="\n"}var blob=new Blob([csvOutput],{type:"text/csv"});if(window.navigator.msSaveOrOpenBlob){window.navigator.msSaveBlob(blob,"results.csv")}else{jQuery("<a/>",{class:"babe-button",html:"Download the results as CSV",href:window.URL.createObjectURL(blob),download:"results.csv"}).appendTo($(".view"))}};const flattenData=function(data){var trials=data.trials;delete data.trials;var sample_trial=trials[0];for(var trial_key in sample_trial){if(sample_trial.hasOwnProperty(trial_key)){if(data.hasOwnProperty(trial_key)){var new_data_key="glb_"+trial_key;data[new_data_key]=data[trial_key];delete data[trial_key]}}}var out=_.map(trials,function(t){return _.merge(t,data)});return out};const getHITData=function(){const url=window.location.href;const qArray=url.split("?");const HITData={};if(qArray[1]===undefined){throw new Error("Cannot get participant' s assignmentId from the URL (happens if the experiment does NOT run on MTurk or MTurkSandbox).")}else{qArray=qArray[1].split("&");for(var i=0;i<qArray.length;i++){HITData[qArray[i].split("=")[0]]=qArray[i].split("=")[1]}}return HITData};return submit}function loop(arr,count,shuffleFlag){return _.flatMapDeep(_.range(count),function(i){return arr})}function loopShuffled(arr,count){return _.flatMapDeep(_.range(count),function(i){return _.shuffle(arr)})}function setTitle(title,dflt){return title===undefined||title===""?dflt:title}function setButtonText(buttonText){return buttonText===undefined||buttonText===""?"Next":buttonText}const babeViews={intro:function(config){paramsChecker(config,"intro");const intro={name:config.name,title:setTitle(config.title,"Welcome!"),text:config.text,button:setButtonText(config.buttonText),render:function(CT,babe){const viewTemplate=`<div class='babe-view'>\n <h1 class='babe-view-title'>${this.title}</h1>\n <section class="babe-text-container">\n <p class="babe-view-text">${this.text}</p>\n </section>\n <p id="prolific-id-form">\n <label for="prolific-id">Please, enter your Prolific ID</label>\n <input type="text" id="prolific-id" />\n </p>\n <button id="next" class='babe-view-button' class="babe-nodisplay">${this.button}</button>\n </div>`;$("#main").html(viewTemplate);const prolificId=$("#prolific-id");const IDform=$("#prolific-id-form");const next=$("#next");function showNextBtn(){if(prolificId.val().trim()!==""){next.removeClass("babe-nodisplay")}else{next.addClass("babe-nodisplay")}}if(babe.deploy.deployMethod!=="Prolific"){IDform.addClass("babe-nodisplay");next.removeClass("babe-nodisplay")}prolificId.on("keyup",function(){showNextBtn()});prolificId.on("focus",function(){showNextBtn()});next.on("click",function(){if(babe.deploy.deployMethod==="Prolific"){babe.global_data.prolific_id=prolificId.val().trim()}babe.findNextView()})},CT:0,trials:config.trials};return intro},instructions:function(config){paramsChecker(config,"instructions");const instructions={name:config.name,title:setTitle(config.title,"Instructions"),text:config.text,button:setButtonText(config.buttonText),render:function(CT,babe){const viewTemplate=`<div class="babe-view">\n <h1 class='babe-view-title'>${this.title}</h1>\n <section class="babe-text-container">\n <p class="babe-view-text">${this.text}</p>\n </section>\n <button id="next" class='babe-view-button'>${this.button}</button>\n </div>`;$("#main").html(viewTemplate);$("#next").on("click",function(){babe.findNextView()})},CT:0,trials:config.trials};return instructions},begin:function(config){paramsChecker(config,"begin experiment");const begin={name:config.name,title:setTitle(config.title,"Begin"),text:config.text,button:setButtonText(config.buttonText),render:function(CT,babe){const viewTemplate=`<div class="babe-view">\n <h1 class='babe-view-title'>${this.title}</h1>\n <section class='babe-text-container'>\n <p class='babe-view-text'>${this.text}</p>\n </section>\n <button id='next' class='babe-view-button'>${this.button}</button>\n </div>`;$("#main").html(viewTemplate);$("#next").on("click",function(e){babe.findNextView()})},CT:0,trials:config.trials};return begin},forcedChoice:function(config){checkTrialView(config,"forced choice");paramsChecker(config,"forced choice");const forcedChoice={name:config.name,render:function(CT,babe){const question=config.data[CT].question===undefined?"":config.data[CT].question;const picture=config.data[CT].picture===undefined?"":config.data[CT].picture;const option1=config.data[CT].option1;const option2=config.data[CT].option2;const viewTemplate=`<div class='babe-view'>\n <div class='babe-view-picture'>\n <img src=${picture} alt='a picture'>\n </div>\n <p class='babe-view-question'>${question}</p>\n <p class='babe-view-answer-container'>\n <label for='o1' class='babe-response-buttons'>${option1}</label>\n <input type='radio' name='answer' id='o1' value=${option1} />\n <input type='radio' name='answer' id='o2' value=${option2} />\n <label for='o2' class='babe-response-buttons'>${option2}</label>\n </p>\n </div>`;$("#main").html(viewTemplate);const startingTime=Date.now();$("input[name=answer]").on("change",function(){const RT=Date.now()-startingTime;const trial_data={trial_type:config.trial_type,trial_number:CT+1,question:config.data[CT].question,picture:config.data[CT].picture,option1:config.data[CT].option1,option2:config.data[CT].option2,response:$("input[name=answer]:checked").val(),RT:RT};babe.trial_data.push(trial_data);babe.findNextView()})},CT:0,trials:config.trials};return forcedChoice},sliderRating:function(config){checkTrialView(config,"slider rating");paramsChecker(config,"slider rating");const sliderRating={name:config.name,render:function(CT,babe){const question=config.data[CT].question===undefined?"":config.data[CT].question;const picture=config.data[CT].picture===undefined?"":config.data[CT].picture;const option1=config.data[CT].option1;const option2=config.data[CT].option2;let response;const viewTemplate=`<div class='babe-view'>\n <div class='babe-view-picture'>\n <img src=${picture} alt='a picture'>\n </div>\n <p class='babe-view-question'>${question}</p>\n <p class='babe-view-answer-container'>\n <span class='babe-response-slider-option'>${option1}</span>\n <input type='range' id='response' class='babe-response-slider' min='0' max='100' value='50'/>\n <span class='babe-response-slider-option'>${option2}</span>\n </p>\n <button id="next" class='babe-view-button babe-nodisplay'>Next</button>\n </div>`;$("#main").html(viewTemplate);const startingTime=Date.now();response=$("#response");response.on("change",function(){$("#next").removeClass("babe-nodisplay")});response.on("click",function(){$("#next").removeClass("babe-nodisplay")});$("#next").on("click",function(){const RT=Date.now()-startingTime;const trial_data={trial_type:config.trial_type,trial_number:CT+1,question:config.data[CT].question,option1:config.data[CT].option1,option2:config.data[CT].option2,picture:config.data[CT].picture,response:response.val(),RT:RT};babe.trial_data.push(trial_data);babe.findNextView()})},CT:0,trials:config.trials};return sliderRating},textboxInput:function(config){checkTrialView(config,"textbox input");paramsChecker(config,"textbox input");const textboxInput={name:config.name,render:function(CT,babe){const question=config.data[CT].question===undefined?"":config.data[CT].question;const picture=config.data[CT].picture===undefined?"":config.data[CT].picture;const minChars=config.data[CT].minChars===undefined?10:config.data[CT].minChars;const viewTemplate=`<div class='babe-view'>\n <div class='babe-view-picture'>\n <img src=${picture} alt='picture'>\n </div>\n <p class='babe-view-question'>${question}</p>\n <p class='babe-view-answer-container'>\n <textarea name='textbox-input' rows=10 cols=50 class='babe-response-text' />\n </p>\n <button id='next' class='babe-view-button babe-nodisplay'>next</button>\n </div>`;$("#main").html(viewTemplate);const next=$("#next");const textInput=$("textarea");const startingTime=Date.now();textInput.on("keyup",function(){if(textInput.val().trim().length>minChars){next.removeClass("babe-nodisplay")}else{next.addClass("babe-nodisplay")}});next.on("click",function(){var RT=Date.now()-startingTime;var trial_data={trial_type:config.trial_type,trial_number:CT+1,question:config.data[CT].question,picture:config.data[CT].picture,minimum_characters:minimum_characters=config.data[CT].minChars,response:textInput.val().trim(),RT:RT};babe.trial_data.push(trial_data);babe.findNextView()})},CT:0,trials:config.trials};return textboxInput},dropdownChoice:function(config){checkTrialView(config,"dropdown choice");paramsChecker(config,"dropdown choice");const dropdownChoice={name:config.name,render:function(CT,babe){let response;const question_left_part=config.data[CT].question_left_part;const question_right_part=config.data[CT].question_right_part===undefined?"":config.data[CT].question_right_part;const picture=config.data[CT].picture===undefined?"":config.data[CT].picture;const option1=config.data[CT].option1;const option2=config.data[CT].option2;const viewTemplate=`<div class='babe-view'>\n <div class='babe-view-picture'>\n <img src=${picture} alt='a picture'>\n </div>\n <p class='babe-view-answer-container babe-response-dropdown'>\n ${question_left_part}\n <select id='response' name='answer'>\n <option disabled selected></option>\n <option value=${option1}>${option1}</option>\n <option value=${option2}>${option2}</option>\n </select>\n ${question_right_part}\n </p>\n <button id='next' class='babe-view-button babe-nodisplay'>Next</button>\n </p>\n </div>`;$("#main").html(viewTemplate);const startingTime=Date.now();response=$("#response");response.on("change",function(){$("#next").removeClass("babe-nodisplay")});$("#next").on("click",function(){const RT=Date.now()-startingTime;const trial_data={trial_type:config.trial_type,trial_number:CT+1,question:question_left_part.concat("...answer here...").concat(question_right_part),picture:config.data[CT].picture,option1:config.data[CT].option1,option2:config.data[CT].option2,response:$(response).val(),RT:RT};babe.trial_data.push(trial_data);babe.findNextView()})},CT:0,trials:config.trials};return dropdownChoice},ratingScale:function(config){checkTrialView(config,"rating scale");paramsChecker(config,"rating scale");const ratingScale={name:config.name,render:function(CT,babe){const question=config.data[CT].question===undefined?"":config.data[CT].question;const picture=config.data[CT].picture===undefined?"":config.data[CT].picture;const option1=config.data[CT].option1;const option2=config.data[CT].option2;const viewTemplate=`<div class='babe-view'>\n <div class='babe-view-picture'>\n <img src=${picture} alt='a picture'>\n </div>\n <p class='babe-view-question'>${question}</p>\n <p class='babe-view-answer-container'>\n <strong class='babe-response-rating-option babe-view-text'>${option1}</strong>\n <label for="1" class='babe-response-rating'>1</label>\n <input type="radio" name="answer" id="1" value="1" />\n <label for="2" class='babe-response-rating'>2</label>\n <input type="radio" name="answer" id="2" value="2" />\n <label for="3" class='babe-response-rating'>3</label>\n <input type="radio" name="answer" id="3" value="3" />\n <label for="4" class='babe-response-rating'>4</label>\n <input type="radio" name="answer" id="4" value="4" />\n <label for="5" class='babe-response-rating'>5</label>\n <input type="radio" name="answer" id="5" value="5" />\n <label for="6" class='babe-response-rating'>6</label>\n <input type="radio" name="answer" id="6" value="6" />\n <label for="7" class='babe-response-rating'>7</label>\n <input type="radio" name="answer" id="7" value="7" />\n <strong class='babe-response-rating-option babe-view-text'>${option2}</strong>\n </p>\n </div>`;$("#main").html(viewTemplate);const startingTime=Date.now();$("input[name=answer]").on("change",function(){const RT=Date.now()-startingTime;const trial_data={trial_type:config.trial_type,trial_number:CT+1,question:config.data[CT].question,picture:config.data[CT].picture,option1:config.data[CT].option1,option2:config.data[CT].option2,response:$("input[name=answer]:checked").val(),RT:RT};babe.trial_data.push(trial_data);babe.findNextView()})},CT:0,trials:config.trials};return ratingScale},sentenceChoice:function(config){checkTrialView(config,"sentence choice");paramsChecker(config,"sentence choice");const sentenceChoice={name:config.name,render:function(CT,babe){const question=config.data[CT].question===undefined?"":config.data[CT].question;const picture=config.data[CT].picture===undefined?"":config.data[CT].picture;const option1=config.data[CT].option1;const option2=config.data[CT].option2;const viewTemplate=`<div class='babe-view'>\n <div class='babe-view-picture'>\n <img src=${picture} alt='a picture'>\n </div>\n <p class='babe-view-question'>${question}</p>\n <p class='babe-view-answer-container'>\n <label for='s1' class='babe-response-sentence'>${option1}</label>\n <input type='radio' name='answer' id='s1' value=${option1}'/>\n <label for='s2' class='babe-response-sentence'>${option2}</label>\n <input type='radio' name='answer' id='s2' value=${option2}"/>\n </p>\n </div>`;$("#main").html(viewTemplate);var startingTime=Date.now();$("input[name=answer]").on("change",function(){var RT=Date.now()-startingTime;var trial_data={trial_type:config.trial_type,trial_number:CT+1,question:config.data[CT].question,picture:config.data[CT].picture,option1:config.data[CT].option1,option2:config.data[CT].option2,response:$("input[name=answer]:checked").val(),RT:RT};babe.trial_data.push(trial_data);babe.findNextView()})},CT:0,trials:config.trials};return sentenceChoice},imageSelection:function(config){checkTrialView(config,"image selection");paramsChecker(config,"image selection");const imageSelection={name:config.name,render:function(CT,babe){const question=config.data[CT].question===undefined?"":config.data[CT].question;const picture1=config.data[CT].picture1;const picture2=config.data[CT].picture2;const option1=config.data[CT].option1;const option2=config.data[CT].option2;const viewTemplate=`<div class="view">\n <p class='babe-view-question'>${question}</p>\n <p class='babe-view-answer-container'>\n <label for="img1" class='babe-view-picture babe-response-picture'><img src=${picture1}></label>\n <input type="radio" name="answer" id="img1" value=${option1} />\n <input type="radio" name="answer" id="img2" value=${option2} />\n <label for="img2" class='babe-view-picture babe-response-picture'><img src=${picture2}></label>\n </p>\n </div>`;$("#main").html(viewTemplate);const startingTime=Date.now();$("input[name=answer]").on("change",function(){const RT=Date.now()-startingTime;const trial_data={trial_type:config.trial_type,trial_number:CT+1,question:config.data[CT].question,option1:config.data[CT].option1,option2:config.data[CT].option2,picture1:config.data[CT].picture1,picture2:config.data[CT].picture2,response:$("input[name=answer]:checked").val(),RT:RT};babe.trial_data.push(trial_data);babe.findNextView()})},CT:0,trials:config.trials};return imageSelection},keyPress:function(config){checkTrialView(config,"key press");paramsChecker(config,"key press");const keyPress={name:config.name,render:function(CT,babe){const question=config.data[CT].question===undefined?"":config.data[CT].question;const picture=config.data[CT].picture===undefined?"":config.data[CT].picture;const key1=config.data[CT].key1;const key2=config.data[CT].key2;const value1=config.data[CT][key1];const value2=config.data[CT][key2];const viewTemplate=`<div class="view">\n <p class='babe-response-keypress-header'><strong>${key1}</strong> = ${value1}, <strong>${key2}</strong> = ${value2}</p>\n <p class='babe-view-question'>${question}</p>\n <div class='babe-view-picture'>\n <img src=${picture} alt='a picture'>\n </div>\n </div>`;$("#main").html(viewTemplate);const startingTime=Date.now();function handleKeyPress(e){const keyPressed=String.fromCharCode(e.which).toLowerCase();if(keyPressed===key1||keyPressed===key2){let correctness;const RT=Date.now()-startingTime;if(config.data[CT].expected===config.data[CT][keyPressed.toLowerCase()]){correctness="correct"}else{correctness="incorrect"}const trial_data={trial_type:config.trial_type,trial_number:CT+1,question:config.data[CT].question,expected:config.data[CT].expected,key_pressed:keyPressed,correctness:correctness,RT:RT};trial_data["key1"]=config.data[CT][key1];trial_data["key2"]=config.data[CT][key2];if(config.data[CT].picture!==undefined){trial_data["picture"]=config.data[CT].picture}if(config.data[CT].question!==undefined){trial_data["question"]=config.data[CT].question}babe.trial_data.push(trial_data);$("body").off("keydown",handleKeyPress);babe.findNextView()}}$("body").on("keydown",handleKeyPress)},CT:0,trials:config.trials};return keyPress},postTest:function(config){paramsChecker(config,"post test");const postTest={name:config.name,title:setTitle(config.title,"Additional Information"),text:config.text,button:setButtonText(config.buttonText),render:function(CT,babe){const viewTemplate=`<div class='babe-view babe-post-test-view'>\n <h1 class='babe-view-title'>${this.title}</h1>\n <section class='babe-text-container'>\n <p class='babe-view-text'>${this.text}</p>\n </section>\n <form>\n <p class='babe-view-text'>\n <label for="age">Age:</label>\n <input type="number" name="age" min="18" max="110" id="age" />\n </p>\n <p class='babe-view-text'>\n <label for="gender">Gender:</label>\n <select id="gender" name="gender">\n <option></option>\n <option value="male">male</option>\n <option value="female">female</option>\n <option value="other">other</option>\n </select>\n </p>\n <p class='babe-view-text'>\n <label for="education">Level of Education:</label>\n <select id="education" name="education">\n <option></option>\n <option value="graduated_high_school">Graduated High School</option>\n <option value="graduated_college">Graduated College</option>\n <option value="higher_degree">Higher Degree</option>\n </select>\n </p>\n <p class='babe-view-text'>\n <label for="languages" name="languages">Native Languages: <br /><span>(i.e. the language(s) spoken at home when you were a child)</</span></label>\n <input type="text" id="languages"/>\n </p>\n <p class="babe-view-text">\n <label for="comments">Further comments</label>\n <textarea name="comments" id="comments"\n rows="6" cols="40"></textarea>\n </p>\n <button id="next" class='babe-view-button'>${this.button}</button>\n </form>\n </div>`;$("#main").html(viewTemplate);$("#next").on("click",function(e){e.preventDefault();babe.global_data.age=$("#age").val();babe.global_data.gender=$("#gender").val();babe.global_data.education=$("#education").val();babe.global_data.languages=$("#languages").val();babe.global_data.comments=$("#comments").val().trim();babe.global_data.endTime=Date.now();babe.global_data.timeSpent=(babe.global_data.endTime-babe.global_data.startTime)/6e4;babe.findNextView()})},CT:0,trials:config.trials};return postTest},thanks:function(config){paramsChecker(config,"thanks");const thanks={name:config.name,title:setTitle(config.title,"Thank you for taking part in this experiment!"),render:function(CT,babe){if(babe.deploy.is_MTurk||babe.deploy.deployMethod==="directLink"){$("#main").html(`<div class='babe-view babe-thanks-view'>\n <h4 id='warning-message' class='babe-warning-message'>submitting the data\n <div class='babe-loader'></div>\n </h4>\n <h1 id='thanks-message' class='babe-nodisplay'>${this.title}</h1>\n </div>`)}else if(babe.deploy.deployMethod==="Prolific"){$("main").html(`<div class='babe-view babe-thanks-view'>\n <h4 class='babe-warning-message'>submitting the data\n <div class='babe-loader'></div>\n </h4>\n <h1 id='thanks-message' class='babe-nodisplay'>${this.title}</h1>\n <h2 id='extra-message' class='babe-nodisplay'>${extraMessage}</h2>\n </div>`)}else if(babe.deploy.deployMethod==="debug"){$("main").html(`<div id='babe-debug-table-container' class='babe-view babe-thanks-view'></div>`)}else{console.error("No such babe.deploy.deployMethod")}babe.submission.submit(babe)},CT:0,trials:1};return thanks}};function paramsChecker(config,view){if(config.trials===undefined||config.trials===""){throw new Error(errors.noTrials.concat(findFile(view)))}if(config.name===undefined||config.name===""){throw new Error(errors.noName.concat(findFile(view)))}}function checkTrialView(config,view){if(config.data===undefined||config.data===null){throw new Error(errors.noData.concat(findFile(view)))}if(config.data instanceof Array===false){throw new Error(errors.notAnArray.concat(findFile(view)))}if(config.trial_type===undefined||config.trial_type===""){throw new Error(errors.noTrialType.concat(findFile(view)))}}function findFile(view){return`\n\nThe problem is in ${view} view.`} | ||
const errors={contactEmail:`There is no contact_email given. Please give a contact_email to the babeInit function,\n\nfor example:\n\nbabeInit({\n ...\n deploy: {\n ...\n contact_email: 'yourcontactemail@email.sample',\n ...\n },\n ...\n});`,prolificURL:`There is no prolificURL given. Please give a prolificURL to the babeInit function,\n\nfor example:\n\nbabeInit({\n ...\n deploy: {\n ...\n prolificURL: 'https://app.prolific.ac/submissions/complete?cc=SAMPLE',\n ...\n },\n ...\n});`,noTrials:`No trials given. Each _babe view takes an object with an obligatory 'trial' property.\n\nfor example:\n\nvar introView = intro({\n ...\n trials: 1,\n ...\n});\n\nYou can find more information at https://github.com/babe-project/babe-base#views-in-_babe`,noName:`No name given. Each _babe view takes an object with an obligatory 'name' property\n\nfor example:\n\nvar introView = intro({\n ...\n name: 'introView',\n ...\n});\n\nYou can find more information at https://github.com/babe-project/babe-base#views-in-_babe`,noData:`No data given. Each _babe view takes an object with an obligatory 'data' property\n\nfor example:\n\nvar mainTrials = forcedChoice({\n ...\n data: my_main_trials,\n ...\n});\n\nThe data is a list of objects defined in your local js file.\n\n_babe's trial views expect each trial object to have specific properties. Here is an example of a forcedCoice view trial:\n\n{\n question: 'How are you today?',\n option1: 'fine',\n option2: 'good'\n}\n\nYou can find more information at https://github.com/babe-project/babe-base#views-in-_babe`,noTrialType:`No trial_type given. Each _babe view takes an object with an obligatory 'trial_type' property\n\nfor example:\n\nvar mainTrials = forcedChoice({\n ...\n trial_type: 'main trials',\n ...\n});\n\nThe trial type is needed for recording the results of your experiment.\n\nYou can find more information at https://github.com/babe-project/babe-base#views-in-_babe`,notAnArray:`The data is not an array. Trial views get an array of objects.\n\nfor example:\n\nvar mainTrials = forcedChoice({\n ...\n data: [\n {\n prop: val,\n prop: val\n },\n {\n prop: val,\n prop:val\n }\n ],\n ...\n});`,noSuchViewName:`The view name listed in progress_bar.in does not exist. Use the view names to reference the views in progress_bar.in.\n\nfor example:\n\nvar mainView = forcedChoice({\n ...\n name: 'myMainView',\n ...\n});\n\nvar introView = intro({\n ...\n name: 'intro',\n ...\n});\n\nbabeInit({\n ...\n progress_bar: {\n in: [\n "myMainView"\n ],\n style: "chunks"\n width: 100\n },\n ...\n});\n`};const babeInit=function(config){const babe={};babe.views_seq=_.flatten(config.views_seq);babe.currentViewCounter=0;babe.currentTrialCounter=0;babe.currentTrialInViewCounter=0;babe.progress_bar=config.progress_bar;babe.global_data={startDate:Date(),startTime:Date.now()};babe.trial_data=[];babe.deploy=config.deploy;babe.deploy.MTurk_server=babe.deploy.deployMethod=="MTurkSandbox"?"https://workersandbox.mturk.com/mturk/externalSubmit":babe.deploy.deployMethod=="MTurk"?"https://www.mturk.com/mturk/externalSubmit":"";babe.deploy.liveExperiment=babe.deploy.deployMethod!=="debug";babe.deploy.is_MTurk=babe.deploy.MTurk_server!=="";babe.deploy.submissionURL=babe.deploy.deployMethod=="localServer"?"http://localhost:4000/api/submit_experiment/"+babe.deploy.experimentID:babe.deploy.serverAppURL+babe.deploy.experimentID;const regex="/submit_experiment/";babe.deploy.checkExperimentURL=babe.deploy.submissionURL.replace(regex,"/check_experiment/");babe.progress=babeProgress(babe);babe.submission=babeSubmit(babe);babe.findNextView=function(){let currentView=babe.views_seq[babe.currentViewCounter];if(babe.currentTrialInViewCounter<currentView.trials){currentView.render(currentView.CT,babe)}else{babe.currentViewCounter++;currentView=babe.views_seq[babe.currentViewCounter];babe.currentTrialInViewCounter=0;if(currentView!==undefined){currentView.render(currentView.CT,babe)}else{$("#main").html(Mustache.render(`<div class='view'>\n <h1 class="title">Nothing more to show</h1>\n </div>`));return}}babe.currentTrialInViewCounter++;babe.currentTrialCounter++;currentView.CT++;if(currentView.hasProgressBar){babe.progress.update()}};(function(){if(babe.deploy.deployMethod==="MTurk"||babe.deploy.deployMethod==="MTurkSandbox"){console.info(`The experiment runs on MTurk (or MTurk's sandbox)\n----------------------------\n\nThe ID of your experiment is ${babe.deploy.experimentID}\n\nThe results will be submitted ${babe.deploy.submissionURL}\n\nand\n\nMTurk's server: ${babe.deploy.MTurk_server}`)}else if(babe.deploy.deployMethod==="Prolific"){console.info(`The experiment runs on Prolific\n-------------------------------\n\nThe ID of your experiment is ${babe.deploy.experimentID}\n\nThe results will be submitted to ${babe.deploy.submissionURL}\n\nwith\n\nProlific URL (must be the same as in the website): ${babe.deploy.prolificURL}`)}else if(babe.deploy.deployMethod==="directLink"){console.info(`The experiment uses Direct Link\n-------------------------------\n\nThe ID of your experiment is ${babe.deploy.experimentID}\n\nThe results will be submitted to ${babe.deploy.submissionURL}`)}else if(babe.deploy.deployMethod==="debug"){console.info(`The experiment is in Debug Mode\n-------------------------------\n\nThe results will be displayed in a table at the end of the experiment and available to download in CSV format.`)}else if(babe.deploy.deployMethod!=="localServer"){throw new Error(`There is no such deployMethod.\n\nPlease use 'debug', 'directLink', 'Mturk', 'MTurkSandbox', 'localServer' or 'Prolific'.\n\nThe deploy method you provided is '${babe.deploy.deployMethod}'.\n\nYou can find more information at https://github.com/babe-project/babe-base`)}if(babe.deploy.deployMethod==="Prolific"&&(babe.deploy.prolificURL===undefined||babe.deploy.prolificURL==="")){throw new Error(errors.prolificURL)}if(babe.deploy.contact_email===undefined||babe.deploy.contact_email===""){throw new Error(errors.contactEmail)}})();if(babe.deploy.deployMethod!=="debug"){$.ajax({type:"GET",url:babe.deploy.checkExperimentURL,crossDomain:true,success:function(responseData,textStatus,jqXHR){babe.progress.add();babe.findNextView()},error:function(jqXHR,textStatus,error){console.log(babe.deploy.checkExperimentURL);alert(`Sorry, there is an error communicating with our server and the experiment cannot proceed. Please return the HIT immediately and contact the author at ${babe.deploy.contact_email}. Please include the following error message: "${jqXHR.responseText}". Thank you for your understanding.`)}})}else{babe.progress.add();babe.findNextView()}};const babeProgress=function(babe){let totalProgressParts=0;let progressTrials=0;let totalProgressChunks=0;let filledChunks=0;let fillChunk=false;const progress={add:function(){babe.views_seq.map(view=>{for(let j=0;j<babe.progress_bar.in.length;j++){if(view.name===babe.progress_bar.in[j]){totalProgressChunks++;totalProgressParts+=view.trials;view.hasProgressBar=true}}})},update:function(){try{addToDOM()}catch(e){console.error(e.message)}const progressBars=$(".progress-bar");let div,filledPart;if(babe.progress_bar.style==="default"){div=$(".progress-bar").width()/totalProgressParts;filledPart=progressTrials*div}else{div=$(".progress-bar").width()/babe.views_seq[babe.currentViewCounter].trials;filledPart=(babe.currentTrialInViewCounter-1)*div}const filledElem=jQuery("<span/>",{id:"filled"}).appendTo(progressBars[filledChunks]);$("#filled").css("width",filledPart);progressTrials++;if(babe.progress_bar.style==="chunks"){if(fillChunk===true){filledChunks++;fillChunk=false}if(filledElem.width()===$(".progress-bar").width()-div){fillChunk=true}for(var i=0;i<filledChunks;i++){progressBars[i].style.backgroundColor="#5187BA"}}}};const addToDOM=function(){var bar;var i;var view=$(".view");var barWidth=babe.progress_bar.width;var clearfix=jQuery("<div/>",{class:"clearfix"});var container=jQuery("<div/>",{class:"progress-bar-container"});view.css("padding-top",30);view.prepend(clearfix);view.prepend(container);if(babe.progress_bar.style==="chunks"){for(i=0;i<totalProgressChunks;i++){bar=jQuery("<div/>",{class:"progress-bar"});bar.css("width",barWidth);container.append(bar)}}else if(babe.progress_bar.style==="separate"){bar=jQuery("<div/>",{class:"progress-bar"});bar.css("width",barWidth);container.append(bar)}else if(babe.progress_bar.style==="default"){bar=jQuery("<div/>",{class:"progress-bar"});bar.css("width",barWidth);container.append(bar)}else{throw new Error('Progress_bar.style can be set to "default", "separate" or "chunks" in experiment.js')}};return progress};function babeSubmit(babe){const submit={submit:function(babe){let data={experiment_id:babe.deploy.experimentID,trials:addEmptyColumns(babe.trial_data)};data=_.merge(babe.global_data,data);if(babe.deploy.is_MTurk){try{const HITData=getHITData();data["assignment_id"]=HITData["assignmentId"];data["worker_id"]=HITData["workerId"];data["hit_id"]=HITData["hitId"];var form=jQuery("<form/>",{id:"mturk-submission-form",action:config_deploy.MTurk_server}).appendTo(".babe-thanks-view");jQuery("<input/>",{type:"hidden",name:"data",value:JSON.stringify(data)}).appendTo(form);jQuery("<input/>",{type:"hidden",name:"assignmentId",value:HITData["assignmentId"]}).appendTo(form)}catch(e){console.error(e)}}if(babe.deploy.liveExperiment){console.log("submits");submitResults(babe.deploy.contact_email,babe.deploy.submissionURL,flattenData(data),babe.deploy)}else{const flattenedData=flattenData(data);jQuery("<div/>",{class:"babe-debug-results",html:formatDebugData(flattenedData)}).appendTo($("#babe-debug-table-container"));createCSVForDownload(flattenedData)}}};function submitResults(contactEmail,submissionURL,data,config){contactEmail=typeof contactEmail!=="undefined"?contactEmail:"not provided";$.ajax({type:"POST",url:submissionURL,crossDomain:true,contentType:"application/json",data:JSON.stringify(data),success:function(responseData,textStatus,jqXHR){console.log(textStatus);$("#warning-message").addClass("babe-nodisplay");$("#thanks-message").removeClass("babe-nodisplay");$("#extra-message").removeClass("babe-nodisplay");if(config.is_MTurk){setTimeout(function(){submitToMTurk(data),500})}},error:function(responseData,textStatus,errorThrown){if(config.is_MTurk){submitToMTurk(data);$("#thanks-message").removeClass("babe-nodisplay")}else{if(textStatus=="timeout"){alert("Oops, the submission timed out. Please try again. If the problem persists, please contact "+contactEmail+", including your ID")}else{alert("Oops, the submission failed. The server says: "+responseData.responseText+"\nPlease try again. If the problem persists, please contact "+contactEmail+"with this error message, including your ID")}}}})}const submitToMTurk=function(){var form=$("#mturk-submission-form");form.submit()};const addEmptyColumns=function(trialData){var columns=[];for(var i=0;i<trialData.length;i++){for(var prop in trialData[i]){if(trialData[i].hasOwnProperty(prop)&&columns.indexOf(prop)===-1){columns.push(prop)}}}for(var j=0;j<trialData.length;j++){for(var k=0;k<columns.length;k++){if(!trialData[j].hasOwnProperty(columns[k])){trialData[j][columns[k]]="NA"}}}return trialData};const formatDebugData=function(flattenedData){var output="<table id='babe-debug-table'>";var t=flattenedData[0];output+="<thead><tr>";for(var key in t){if(t.hasOwnProperty(key)){output+="<th>"+key+"</th>"}}output+="</tr></thead>";output+="<tbody><tr>";var entry="";for(var i=0;i<flattenedData.length;i++){var currentTrial=flattenedData[i];for(var k in t){if(currentTrial.hasOwnProperty(k)){entry=String(currentTrial[k]);output+="<td>"+entry.replace(/ /g," ")+"</td>"}}output+="</tr>"}output+="</tbody></table>";return output};const createCSVForDownload=function(flattenedData){var csvOutput="";var t=flattenedData[0];for(var key in t){if(t.hasOwnProperty(key)){csvOutput+='"'+String(key)+'",'}}csvOutput+="\n";for(var i=0;i<flattenedData.length;i++){var currentTrial=flattenedData[i];for(var k in t){if(currentTrial.hasOwnProperty(k)){csvOutput+='"'+String(currentTrial[k])+'",'}}csvOutput+="\n"}var blob=new Blob([csvOutput],{type:"text/csv"});if(window.navigator.msSaveOrOpenBlob){window.navigator.msSaveBlob(blob,"results.csv")}else{jQuery("<a/>",{class:"babe-button",html:"Download the results as CSV",href:window.URL.createObjectURL(blob),download:"results.csv"}).appendTo($(".view"))}};const flattenData=function(data){var trials=data.trials;delete data.trials;var sample_trial=trials[0];for(var trial_key in sample_trial){if(sample_trial.hasOwnProperty(trial_key)){if(data.hasOwnProperty(trial_key)){var new_data_key="glb_"+trial_key;data[new_data_key]=data[trial_key];delete data[trial_key]}}}var out=_.map(trials,function(t){return _.merge(t,data)});return out};const getHITData=function(){const url=window.location.href;const qArray=url.split("?");const HITData={};if(qArray[1]===undefined){throw new Error("Cannot get participant' s assignmentId from the URL (happens if the experiment does NOT run on MTurk or MTurkSandbox).")}else{qArray=qArray[1].split("&");for(var i=0;i<qArray.length;i++){HITData[qArray[i].split("=")[0]]=qArray[i].split("=")[1]}}return HITData};return submit}function loop(arr,count,shuffleFlag){return _.flatMapDeep(_.range(count),function(i){return arr})}function loopShuffled(arr,count){return _.flatMapDeep(_.range(count),function(i){return _.shuffle(arr)})}function setTitle(title,dflt){return title===undefined||title===""?dflt:title}function setButtonText(buttonText){return buttonText===undefined||buttonText===""?"Next":buttonText}const babeViews={intro:function(config){paramsChecker(config,"intro");const intro={name:config.name,title:setTitle(config.title,"Welcome!"),text:config.text,button:setButtonText(config.buttonText),render:function(CT,babe){const viewTemplate=`<div class='babe-view'>\n <h1 class='babe-view-title'>${this.title}</h1>\n <section class="babe-text-container">\n <p class="babe-view-text">${this.text}</p>\n </section>\n <p id="prolific-id-form">\n <label for="prolific-id">Please, enter your Prolific ID</label>\n <input type="text" id="prolific-id" />\n </p>\n <button id="next" class='babe-view-button' class="babe-nodisplay">${this.button}</button>\n </div>`;$("#main").html(viewTemplate);const prolificId=$("#prolific-id");const IDform=$("#prolific-id-form");const next=$("#next");function showNextBtn(){if(prolificId.val().trim()!==""){next.removeClass("babe-nodisplay")}else{next.addClass("babe-nodisplay")}}if(babe.deploy.deployMethod!=="Prolific"){IDform.addClass("babe-nodisplay");next.removeClass("babe-nodisplay")}prolificId.on("keyup",function(){showNextBtn()});prolificId.on("focus",function(){showNextBtn()});next.on("click",function(){if(babe.deploy.deployMethod==="Prolific"){babe.global_data.prolific_id=prolificId.val().trim()}babe.findNextView()})},CT:0,trials:config.trials};return intro},instructions:function(config){paramsChecker(config,"instructions");const instructions={name:config.name,title:setTitle(config.title,"Instructions"),text:config.text,button:setButtonText(config.buttonText),render:function(CT,babe){const viewTemplate=`<div class="babe-view">\n <h1 class='babe-view-title'>${this.title}</h1>\n <section class="babe-text-container">\n <p class="babe-view-text">${this.text}</p>\n </section>\n <button id="next" class='babe-view-button'>${this.button}</button>\n </div>`;$("#main").html(viewTemplate);$("#next").on("click",function(){babe.findNextView()})},CT:0,trials:config.trials};return instructions},begin:function(config){paramsChecker(config,"begin experiment");const begin={name:config.name,title:setTitle(config.title,"Begin"),text:config.text,button:setButtonText(config.buttonText),render:function(CT,babe){const viewTemplate=`<div class="babe-view">\n <h1 class='babe-view-title'>${this.title}</h1>\n <section class='babe-text-container'>\n <p class='babe-view-text'>${this.text}</p>\n </section>\n <button id='next' class='babe-view-button'>${this.button}</button>\n </div>`;$("#main").html(viewTemplate);$("#next").on("click",function(e){babe.findNextView()})},CT:0,trials:config.trials};return begin},forcedChoice:function(config){checkTrialView(config,"forced choice");paramsChecker(config,"forced choice");const forcedChoice={name:config.name,render:function(CT,babe){const question=config.data[CT].question===undefined?"":config.data[CT].question;const picture=config.data[CT].picture===undefined?"":config.data[CT].picture;const option1=config.data[CT].option1;const option2=config.data[CT].option2;const viewTemplate=`<div class='babe-view'>\n <div class='babe-view-picture'>\n <img src=${picture} alt='a picture'>\n </div>\n <p class='babe-view-question'>${question}</p>\n <p class='babe-view-answer-container'>\n <label for='o1' class='babe-response-buttons'>${option1}</label>\n <input type='radio' name='answer' id='o1' value=${option1} />\n <input type='radio' name='answer' id='o2' value=${option2} />\n <label for='o2' class='babe-response-buttons'>${option2}</label>\n </p>\n </div>`;$("#main").html(viewTemplate);const startingTime=Date.now();$("input[name=answer]").on("change",function(){const RT=Date.now()-startingTime;const trial_data={trial_type:config.trial_type,trial_number:CT+1,question:config.data[CT].question,picture:config.data[CT].picture,option1:config.data[CT].option1,option2:config.data[CT].option2,response:$("input[name=answer]:checked").val(),RT:RT};babe.trial_data.push(trial_data);babe.findNextView()})},CT:0,trials:config.trials};return forcedChoice},sliderRating:function(config){checkTrialView(config,"slider rating");paramsChecker(config,"slider rating");const sliderRating={name:config.name,render:function(CT,babe){const question=config.data[CT].question===undefined?"":config.data[CT].question;const picture=config.data[CT].picture===undefined?"":config.data[CT].picture;const option1=config.data[CT].option1;const option2=config.data[CT].option2;let response;const viewTemplate=`<div class='babe-view'>\n <div class='babe-view-picture'>\n <img src=${picture} alt='a picture'>\n </div>\n <p class='babe-view-question'>${question}</p>\n <p class='babe-view-answer-container'>\n <span class='babe-response-slider-option'>${option1}</span>\n <input type='range' id='response' class='babe-response-slider' min='0' max='100' value='50'/>\n <span class='babe-response-slider-option'>${option2}</span>\n </p>\n <button id="next" class='babe-view-button babe-nodisplay'>Next</button>\n </div>`;$("#main").html(viewTemplate);const startingTime=Date.now();response=$("#response");response.on("change",function(){$("#next").removeClass("babe-nodisplay")});response.on("click",function(){$("#next").removeClass("babe-nodisplay")});$("#next").on("click",function(){const RT=Date.now()-startingTime;const trial_data={trial_type:config.trial_type,trial_number:CT+1,question:config.data[CT].question,option1:config.data[CT].option1,option2:config.data[CT].option2,picture:config.data[CT].picture,response:response.val(),RT:RT};babe.trial_data.push(trial_data);babe.findNextView()})},CT:0,trials:config.trials};return sliderRating},textboxInput:function(config){checkTrialView(config,"textbox input");paramsChecker(config,"textbox input");const textboxInput={name:config.name,render:function(CT,babe){const question=config.data[CT].question===undefined?"":config.data[CT].question;const picture=config.data[CT].picture===undefined?"":config.data[CT].picture;const minChars=config.data[CT].minChars===undefined?10:config.data[CT].minChars;const viewTemplate=`<div class='babe-view'>\n <div class='babe-view-picture'>\n <img src=${picture} alt='picture'>\n </div>\n <p class='babe-view-question'>${question}</p>\n <p class='babe-view-answer-container'>\n <textarea name='textbox-input' rows=10 cols=50 class='babe-response-text' />\n </p>\n <button id='next' class='babe-view-button babe-nodisplay'>next</button>\n </div>`;$("#main").html(viewTemplate);const next=$("#next");const textInput=$("textarea");const startingTime=Date.now();textInput.on("keyup",function(){if(textInput.val().trim().length>minChars){next.removeClass("babe-nodisplay")}else{next.addClass("babe-nodisplay")}});next.on("click",function(){var RT=Date.now()-startingTime;var trial_data={trial_type:config.trial_type,trial_number:CT+1,question:config.data[CT].question,picture:config.data[CT].picture,minimum_characters:config.data[CT].minChars,response:textInput.val().trim(),RT:RT};babe.trial_data.push(trial_data);babe.findNextView()})},CT:0,trials:config.trials};return textboxInput},dropdownChoice:function(config){checkTrialView(config,"dropdown choice");paramsChecker(config,"dropdown choice");const dropdownChoice={name:config.name,render:function(CT,babe){let response;const question_left_part=config.data[CT].question_left_part;const question_right_part=config.data[CT].question_right_part===undefined?"":config.data[CT].question_right_part;const picture=config.data[CT].picture===undefined?"":config.data[CT].picture;const option1=config.data[CT].option1;const option2=config.data[CT].option2;const viewTemplate=`<div class='babe-view'>\n <div class='babe-view-picture'>\n <img src=${picture} alt='a picture'>\n </div>\n <p class='babe-view-answer-container babe-response-dropdown'>\n ${question_left_part}\n <select id='response' name='answer'>\n <option disabled selected></option>\n <option value=${option1}>${option1}</option>\n <option value=${option2}>${option2}</option>\n </select>\n ${question_right_part}\n </p>\n <button id='next' class='babe-view-button babe-nodisplay'>Next</button>\n </p>\n </div>`;$("#main").html(viewTemplate);const startingTime=Date.now();response=$("#response");response.on("change",function(){$("#next").removeClass("babe-nodisplay")});$("#next").on("click",function(){const RT=Date.now()-startingTime;const trial_data={trial_type:config.trial_type,trial_number:CT+1,question:question_left_part.concat("...answer here...").concat(question_right_part),picture:config.data[CT].picture,option1:config.data[CT].option1,option2:config.data[CT].option2,response:$(response).val(),RT:RT};babe.trial_data.push(trial_data);babe.findNextView()})},CT:0,trials:config.trials};return dropdownChoice},ratingScale:function(config){checkTrialView(config,"rating scale");paramsChecker(config,"rating scale");const ratingScale={name:config.name,render:function(CT,babe){const question=config.data[CT].question===undefined?"":config.data[CT].question;const picture=config.data[CT].picture===undefined?"":config.data[CT].picture;const option1=config.data[CT].option1;const option2=config.data[CT].option2;const viewTemplate=`<div class='babe-view'>\n <div class='babe-view-picture'>\n <img src=${picture} alt='a picture'>\n </div>\n <p class='babe-view-question'>${question}</p>\n <p class='babe-view-answer-container'>\n <strong class='babe-response-rating-option babe-view-text'>${option1}</strong>\n <label for="1" class='babe-response-rating'>1</label>\n <input type="radio" name="answer" id="1" value="1" />\n <label for="2" class='babe-response-rating'>2</label>\n <input type="radio" name="answer" id="2" value="2" />\n <label for="3" class='babe-response-rating'>3</label>\n <input type="radio" name="answer" id="3" value="3" />\n <label for="4" class='babe-response-rating'>4</label>\n <input type="radio" name="answer" id="4" value="4" />\n <label for="5" class='babe-response-rating'>5</label>\n <input type="radio" name="answer" id="5" value="5" />\n <label for="6" class='babe-response-rating'>6</label>\n <input type="radio" name="answer" id="6" value="6" />\n <label for="7" class='babe-response-rating'>7</label>\n <input type="radio" name="answer" id="7" value="7" />\n <strong class='babe-response-rating-option babe-view-text'>${option2}</strong>\n </p>\n </div>`;$("#main").html(viewTemplate);const startingTime=Date.now();$("input[name=answer]").on("change",function(){const RT=Date.now()-startingTime;const trial_data={trial_type:config.trial_type,trial_number:CT+1,question:config.data[CT].question,picture:config.data[CT].picture,option1:config.data[CT].option1,option2:config.data[CT].option2,response:$("input[name=answer]:checked").val(),RT:RT};babe.trial_data.push(trial_data);babe.findNextView()})},CT:0,trials:config.trials};return ratingScale},sentenceChoice:function(config){checkTrialView(config,"sentence choice");paramsChecker(config,"sentence choice");const sentenceChoice={name:config.name,render:function(CT,babe){const question=config.data[CT].question===undefined?"":config.data[CT].question;const picture=config.data[CT].picture===undefined?"":config.data[CT].picture;const option1=config.data[CT].option1;const option2=config.data[CT].option2;const viewTemplate=`<div class='babe-view'>\n <div class='babe-view-picture'>\n <img src=${picture} alt='a picture'>\n </div>\n <p class='babe-view-question'>${question}</p>\n <p class='babe-view-answer-container'>\n <label for='s1' class='babe-response-sentence'>${option1}</label>\n <input type='radio' name='answer' id='s1' value=${option1}'/>\n <label for='s2' class='babe-response-sentence'>${option2}</label>\n <input type='radio' name='answer' id='s2' value=${option2}"/>\n </p>\n </div>`;$("#main").html(viewTemplate);var startingTime=Date.now();$("input[name=answer]").on("change",function(){var RT=Date.now()-startingTime;var trial_data={trial_type:config.trial_type,trial_number:CT+1,question:config.data[CT].question,picture:config.data[CT].picture,option1:config.data[CT].option1,option2:config.data[CT].option2,response:$("input[name=answer]:checked").val(),RT:RT};babe.trial_data.push(trial_data);babe.findNextView()})},CT:0,trials:config.trials};return sentenceChoice},imageSelection:function(config){checkTrialView(config,"image selection");paramsChecker(config,"image selection");const imageSelection={name:config.name,render:function(CT,babe){const question=config.data[CT].question===undefined?"":config.data[CT].question;const picture1=config.data[CT].picture1;const picture2=config.data[CT].picture2;const option1=config.data[CT].option1;const option2=config.data[CT].option2;const viewTemplate=`<div class="view">\n <p class='babe-view-question'>${question}</p>\n <p class='babe-view-answer-container'>\n <label for="img1" class='babe-view-picture babe-response-picture'><img src=${picture1}></label>\n <input type="radio" name="answer" id="img1" value=${option1} />\n <input type="radio" name="answer" id="img2" value=${option2} />\n <label for="img2" class='babe-view-picture babe-response-picture'><img src=${picture2}></label>\n </p>\n </div>`;$("#main").html(viewTemplate);const startingTime=Date.now();$("input[name=answer]").on("change",function(){const RT=Date.now()-startingTime;const trial_data={trial_type:config.trial_type,trial_number:CT+1,question:config.data[CT].question,option1:config.data[CT].option1,option2:config.data[CT].option2,picture1:config.data[CT].picture1,picture2:config.data[CT].picture2,response:$("input[name=answer]:checked").val(),RT:RT};babe.trial_data.push(trial_data);babe.findNextView()})},CT:0,trials:config.trials};return imageSelection},keyPress:function(config){checkTrialView(config,"key press");paramsChecker(config,"key press");const keyPress={name:config.name,render:function(CT,babe){const question=config.data[CT].question===undefined?"":config.data[CT].question;const picture=config.data[CT].picture===undefined?"":config.data[CT].picture;const key1=config.data[CT].key1;const key2=config.data[CT].key2;const value1=config.data[CT][key1];const value2=config.data[CT][key2];const viewTemplate=`<div class="view">\n <p class='babe-response-keypress-header'><strong>${key1}</strong> = ${value1}, <strong>${key2}</strong> = ${value2}</p>\n <p class='babe-view-question'>${question}</p>\n <div class='babe-view-picture'>\n <img src=${picture} alt='a picture'>\n </div>\n </div>`;$("#main").html(viewTemplate);const startingTime=Date.now();function handleKeyPress(e){const keyPressed=String.fromCharCode(e.which).toLowerCase();if(keyPressed===key1||keyPressed===key2){let correctness;const RT=Date.now()-startingTime;if(config.data[CT].expected===config.data[CT][keyPressed.toLowerCase()]){correctness="correct"}else{correctness="incorrect"}const trial_data={trial_type:config.trial_type,trial_number:CT+1,question:config.data[CT].question,expected:config.data[CT].expected,key_pressed:keyPressed,correctness:correctness,RT:RT};trial_data["key1"]=config.data[CT][key1];trial_data["key2"]=config.data[CT][key2];if(config.data[CT].picture!==undefined){trial_data["picture"]=config.data[CT].picture}if(config.data[CT].question!==undefined){trial_data["question"]=config.data[CT].question}babe.trial_data.push(trial_data);$("body").off("keydown",handleKeyPress);babe.findNextView()}}$("body").on("keydown",handleKeyPress)},CT:0,trials:config.trials};return keyPress},postTest:function(config){paramsChecker(config,"post test");const postTest={name:config.name,title:setTitle(config.title,"Additional Information"),text:config.text,button:setButtonText(config.buttonText),render:function(CT,babe){const viewTemplate=`<div class='babe-view babe-post-test-view'>\n <h1 class='babe-view-title'>${this.title}</h1>\n <section class='babe-text-container'>\n <p class='babe-view-text'>${this.text}</p>\n </section>\n <form>\n <p class='babe-view-text'>\n <label for="age">Age:</label>\n <input type="number" name="age" min="18" max="110" id="age" />\n </p>\n <p class='babe-view-text'>\n <label for="gender">Gender:</label>\n <select id="gender" name="gender">\n <option></option>\n <option value="male">male</option>\n <option value="female">female</option>\n <option value="other">other</option>\n </select>\n </p>\n <p class='babe-view-text'>\n <label for="education">Level of Education:</label>\n <select id="education" name="education">\n <option></option>\n <option value="graduated_high_school">Graduated High School</option>\n <option value="graduated_college">Graduated College</option>\n <option value="higher_degree">Higher Degree</option>\n </select>\n </p>\n <p class='babe-view-text'>\n <label for="languages" name="languages">Native Languages: <br /><span>(i.e. the language(s) spoken at home when you were a child)</</span></label>\n <input type="text" id="languages"/>\n </p>\n <p class="babe-view-text">\n <label for="comments">Further comments</label>\n <textarea name="comments" id="comments"\n rows="6" cols="40"></textarea>\n </p>\n <button id="next" class='babe-view-button'>${this.button}</button>\n </form>\n </div>`;$("#main").html(viewTemplate);$("#next").on("click",function(e){e.preventDefault();babe.global_data.age=$("#age").val();babe.global_data.gender=$("#gender").val();babe.global_data.education=$("#education").val();babe.global_data.languages=$("#languages").val();babe.global_data.comments=$("#comments").val().trim();babe.global_data.endTime=Date.now();babe.global_data.timeSpent=(babe.global_data.endTime-babe.global_data.startTime)/6e4;babe.findNextView()})},CT:0,trials:config.trials};return postTest},thanks:function(config){paramsChecker(config,"thanks");const thanks={name:config.name,title:setTitle(config.title,"Thank you for taking part in this experiment!"),render:function(CT,babe){if(babe.deploy.is_MTurk||babe.deploy.deployMethod==="directLink"){$("#main").html(`<div class='babe-view babe-thanks-view'>\n <h2 id='warning-message' class='babe-warning'>Submitting the data\n <p class='babe-view-text'>please do not close the tab</p>\n <div class='babe-loader'></div>\n </h2>\n <h1 id='thanks-message' class='babe-thanks babe-nodisplay'>${this.title}</h1>\n </div>`)}else if(babe.deploy.deployMethod==="Prolific"){$("#main").html(`<div class='babe-view babe-thanks-view'>\n <h2 id='warning-message' class='babe-warning'>Submitting the data\n <p class='babe-view-text'>please do not close the tab</p>\n <div class='babe-loader'></div>\n </h2>\n <h1 id='thanks-message' class='babe-thanks babe-nodisplay'>${this.title}</h1>\n <p id='extra-message' class='babe-view-text babe-nodisplay'>\n Please press the button below to confirm that you completed the experiment with Prolific\n <a href="babe.deploy.prolificURL" class="babe-view-button prolific-url">Confirm</a>\n </p>\n </div>`)}else if(babe.deploy.deployMethod==="debug"){$("main").html(`<div id='babe-debug-table-container' class='babe-view babe-thanks-view'>\n <h1 class='babe-view-title'>Debug Mode</h1>\n </div>`)}else{console.error("No such babe.deploy.deployMethod")}babe.submission.submit(babe)},CT:0,trials:1};return thanks}};function paramsChecker(config,view){if(config.trials===undefined||config.trials===""){throw new Error(errors.noTrials.concat(findFile(view)))}if(config.name===undefined||config.name===""){throw new Error(errors.noName.concat(findFile(view)))}}function checkTrialView(config,view){if(config.data===undefined||config.data===null){throw new Error(errors.noData.concat(findFile(view)))}if(config.data instanceof Array===false){throw new Error(errors.notAnArray.concat(findFile(view)))}if(config.trial_type===undefined||config.trial_type===""){throw new Error(errors.noTrialType.concat(findFile(view)))}}function findFile(view){return`\n\nThe problem is in ${view} view.`} |
{ | ||
"name": "babe-project", | ||
"version": "0.0.14", | ||
"version": "0.0.15", | ||
"description": "Basic Architecture for Browser-based experiments (https://github.com/babe-project/babe-project)", | ||
@@ -5,0 +5,0 @@ "main": "_babe.min.js", |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
497779