// jQuizMe 2.2 by Larry Battle.
//Please give me feedback at blarry@bateru.com
//Copyright (c) 2010 Larry Battle 12/30/2010
//Dual licensed under the MIT and GPL licenses.
//http://www.opensource.org/licenses/mit-license.php
//http://www.gnu.org/licenses/gpl.html
// quizData =[ { ques: "", ans : ""}, {ques: "", ans : ""}, etc ];
// quiz( quizData, options ) # wordsList is an arrary of objects with the following format
(function($){
var _jQuizMeLayOut = $("
").addClass( "quiz-el").append(
$("").addClass( "q-header q-innerArea").append(
$("").addClass( "q-counter"),
$("").addClass( "q-title")
),
$("").addClass( "q-help q-innerArea").append(
$("").addClass( "q-help-menu" ).append(
$( "" ).addClass( "q-quit-area" ).append(
$( "" ).addClass( "q-quit-btn" ),
$( "" ).addClass( "q-quitYes-btn q-quit-confirm" ).hide(),
$( "" ).addClass( "q-quitNo-btn q-quit-confirm" ).hide()
),
$("").addClass( "q-help-btn" ),
$( "" ).addClass( "q-timer-area" ),
$("").addClass( "q-help-info" ).hide()
)
),
$("").addClass( "q-review-menu q-innerArea").append(
$("").addClass( "q-details-btn q-reviewBar-btns" ),
$("").addClass( "q-review-btn q-reviewBar-btns" ),
$("").addClass( "q-reviewBar q-innerArea").append(
$("").attr({ "class": "q-leftArrow q-review-arrows", "value": "<-" }),
$("").attr({ "class": "q-rightArrow q-review-arrows", "value": "->" }),
$( "" ).addClass( "q-review-nav" ).append(
$("").addClass( "q-review-index-all q-review-index"),
$("").addClass( "q-review-index-missed q-review-index").hide(),
$("").addClass( "q-missMarker" ).show(),
$("").addClass( "q-showOnlyMissed-btn")
)
).hide()
),
$("").addClass( "q-intro q-innerArea" ).append(
$( '' ).addClass( "q-intro-info" ),
$( '' ).addClass( "q-begin-btn" )
).hide(),
$("").addClass( "q-prob q-innerArea" ).append(
$("").addClass( "q-ques q-probArea"),
$("").addClass( "q-ans q-probArea").append(
$( "" ).addClass( "q-ansSel" ),
$( "" ).addClass( "q-check-btn" ),
$( "" ).addClass( "q-next-btn" )
),
$("").addClass( "q-result q-probArea" ).hide()
),
$("").addClass( "q-gameOver q-innerArea" ).append(
$("").addClass( "q-stat q-probArea").append(
$("").addClass( "q-statTotal q-center"),
$(""),
$("").addClass( "q-statDetails"),
$("").addClass( "q-extraStat" )
),
$("").addClass( "q-options q-probArea q-center").append(
$("").addClass( "q-restart-btn" ),
$("").addClass( "q-del-btn" )
)
).hide()
);
// Note: areArraysSame will fail when there is an object within the array.
var areArraysSame = function( mainArr, testArr) {
if ( mainArr.length != testArr.length ){ return false; }
var i = mainArr.length;
while( i-- ) {
if ( ( isArray( mainArr[i] ) && !areArraysSame( mainArr[i], testArr[i] ) ) || mainArr[i] !== testArr[i] ) {
return false;
}
}
return true;
},
getStrWithSeeableHTML = function( str, allow ) {
if( !allow || typeof str !== "string" ){ return str; }
return str.replace( /Prosím opakujte odpověď.',
whyAns: "Info:",
yourAns: "Vaše odpověď:"
},
btn:{ // [ "text", "title" ]
begin: [ "Začátek testu" ],
check: [ "Kontrola", "Zjistit správnou odpověď" ],
del: [ "Delete", "Delete quiz" ],
help: [ "Nápověda"],
next: [ "Dál", "Další otázka" ],
restart: [ "Restart", "Restart testu" ],
details: [ "Detaily", "Skóre odpovědi" ],
review: [ "Přehled", "Přehled otázek" ],
showOnlyMissed: [ " *", "Zobrazit jenom nesprávne odpovědi." ],
quit: [ "Konec", "Ukončit test" ],
quitNo: [ "->", "Zpět" ],
quitYes: [ '', "Ano, ukončit" ]
},
err:{
ansInfoNotDefined: "Property ansInfo must be a string and be defined when ansSelInfo is defined.",
badAnsSelInfoLen: "Property ansSel and ansSelInfo must be the same type and have the same length if an array.",
badQType: "Invalid quiz type.",
badKey: " is an invalid key value.",
error: "Error",
noQType: "No quizTypes.",
noQues: "Quiz has no questions.",
notArr: "Must be an array.",
notObj: "Invalid quiz data structure. Must be an array or object."
},
stats:{
right: "Správně",
rate: "Poměr",
score: "Skóre",
wrong: "Špatně",
total: "Celkem",
tried: "Testováno"
},
quiz:{
tfEqual: " = ",
tfEnd : "?",
tfFalse: "False",
tfTrue: "True"
}
},
setLangBtnTxt = function(langBtn, layout) {
var btnCls = [ "begin", "check", "del", "help", "next", "restart", "quit", "quitYes", "quitNo", "review", "details", "showOnlyMissed" ];
var i = btnCls.length,
el;
// !! Replace showOnlyMissed with an input button value="show only wrong" || "show right"
// !! Are delete showOnlyMisssed and have * for the missed questions.
if (!langBtn.quitYes[0]) {
langBtn.quitYes[0] = langBtn.quit[0] + "?";
}
$(".q-missMarker", layout).html(langBtn.showOnlyMissed[0]);
langBtn.showOnlyMissed[0] = '';
while (i--) {
el = [".q-", btnCls[i], "-btn"].join('');
$(el, layout).attr("value", langBtn[btnCls[i]][0]);
$(el, layout).attr("title", langBtn[btnCls[i]][1]);
}
return layout;
},
_settings = {
addToEnd: "", // This is attached to fill in the blank quiz types.
activeClass: "q-ol-active", // Used on multiple choice, (multiOl), quiz types.
allQuizType: '', // This sets all questions to this quiz type.
allRandom: false, // Randomizes all the questions, regardless of quiz type section.
alwaysShowAnsInfo: false, // Shows answer information when correct or incorrect answer are received.
disableRestart: false, // Hide or show the restrart button on gameOver.
disableDelete: true, // Hide or show the delete button on gameOver.
enableRetry: false, // Allows the user to repeat a problem before advancing to next question.
fxType: 0, // animateType [ show/hide, fadeToggle, slideToggle, weight&heightToggle ];
fxCode: false, //If a function, then this is used for animation. Please refer to animateType[] for examples.
fxSpeed: "normal", // "fast", "slow", "normal" or numbers.
help: '', // Provide help text/html if needed.
hoverClass: "q-ol-hover", // Used on multiple choice, (multiOl), quiz types.
intro: '', // Provide an text/html intro.
multiLen : 3, // Set the number of multiple choice choices for quizType multi & multiList.
numOfQuizQues: 0, // Sets the number of questions asked. Must be between 0 and the total questions.
performErrorChecking : true, // Check all the quiz formats.
random: false, // Randomizes all the questions in each quiz type section.
review: true, // Allows for review of questions at gameOver.
showFeedback: true, // Show the answers after each question.
showAnsInfo: true, // If provided, show the answers information after each question.
showHTML: false, //This will show the HTML, by converting to unicode, rather than render it.
showWrongAns: false, // If answer is wrong, then show the user's wrong answer after each question.
statusUpdate: false, // Sends a status Update. Refer to function sendStatus.
quizType: "fillInTheBlank", // This is only need if you send in an array and not a object with a defined quiz type.
title: 'jQuizMe' // title displayed for quiz.
};
$.fn.jQuizMe = function( wordList, options, userLang ){
var settings = $.extend({},_settings, options),
lang = $.extend(true, {},_lang, userLang),
layout = setLangBtnTxt( lang.btn, _jQuizMeLayOut.clone(true));
return this.each( function(){
// currQuiz is the output file.(this is what the user sees). $( el, currQuiz) must be used when accessing elements.
// Hide currQuiz until it's ready to be displayed.
var currQuiz = layout.clone(true).hide(), currIndex = 0, stickyEl = this, quit = false, totalQuesLen = 0,
// The q object is where the quiz data, that was made before the quiz starts, is stored here.
// Please beware that all the questions are stored linearly. But you can find a quiz type by using index[Max|Min].
q = {
// Each index is a question. [ question1, question2, question3,... ]. But not for indexMax|Min and props.
ans: [], // Answers.
ansSel: [], // Answer selections, the elements for inputting an answer. Ex. input text box, or a select tag.
ansSelInfo: [], // Used to show specific feedback for ansSel choices.
ansInfo: [], // Stores the answer information if provided.
retryCount: [], // Stores the number of retries that a user can have.
indexMax: [], // The stopping point index for a quiz type inside the q. ans, ansSel, ansInfo and ques.
indexMin: [], // The starting point index.
prop:[], // Stores the quiz types names in order. Used for creation of each.
ques: [] // Questions.
},
stats = {
indexSelected: [], // indexSelected is only used when ansSelInfo is used.
problem: {},
numOfRight: 0,
numOfWrong : 0,
quesTried: 0,
totalQues: 0,
accur : function(){
var x = Math.round( this.numOfRight / this.quesTried * 100 );
return ( this.quesTried ) ? x : 0;
},
accurTxt : function(){
return [ this.numOfRight, "/", this.quesTried," = ", this.accur(), "%"].join('');
},
perc : function(){
var x = Math.round( this.numOfRight / this.totalQues * 100 );
return ( this.totalQues ) ? x : 0;
},
percTxt : function(){
return [ this.numOfRight, "/", this.numOfQues, " = ", this.perc(), "%"].join('');
},
reset : function(){
this.totalQues = totalQuesLen;
this.quesTried = 0;
this.numOfRight = 0;
this.numOfWrong = 0;
this.problem = {};
this.indexSelected = [];
}
},
// rAns(): Returns a random answer from either all the questions, or a curtain quiz type.
// iProp -> index of the desired quiz type.
rAns = function( iProp, fromAllQues ){
var p = ( fromAllQues ) ? q.prop[ rNum( q.prop.length ) ] : q.prop[ iProp ],
ques = wordList[ p ][ rNum( wordList[ p ].length ) ];
return ques.ans;
},
disableMenuBar = function(){
$( ".q-quit-btn, .q-help-btn", currQuiz ).attr( "disabled", true );
$( ".q-help-info", currQuiz ).hide();
},
haveCorrAnsWithinRetries = function( isFlashCard ){
return ( ((getProblemProp( "amountTried" ) - 1) <= q.retryCount[ currIndex ]) && checkAns( isFlashCard ));
},
letUserAnswerAgain = function( isFlashCard ){
return (settings.enableRetry && getProblemProp( "amountTried" ) < q.retryCount[ currIndex ] && !checkAns( isFlashCard ));
},
setCheckBtnToBeBlockedUntilAnsSelIsClicked = function(){
$( ".q-check-btn", currQuiz ).attr( "disabled", true );
$( ".userInputArea", currQuiz ).one( "click", function(){
$( ".q-check-btn", currQuiz ).attr( "disabled", function(){
return !$( ".q-next-btn", currQuiz ).attr( "disabled" );
});
});
},
displayRetryMsg = function(){
var show = lang.ans.retry;
if( settings.showAnsInfo ){
show += getAnsInfoForDisplay();
}
displayFeedback( show );
},
// setCheckAndNextBtn(): Sets the check button to toggle from "check" to "next". Or just displays "next".
// "check" shows the answer results, but "next" just changes the question.
setCheckAndNextBtn = function(){
var isFlashCard = '';
$( ".q-check-btn", currQuiz ).attr( "disabled", true )
.click( function( e ){
isFlashCard = $( ".userInputArea", currQuiz ).triggerHandler( "getUserAns" );
getStatsProblem().amountTried++;
if( getProblemProp( "amountTried" ) == 1){
stats.quesTried++;
}
if( !letUserAnswerAgain( isFlashCard ) ){
stats[ ( checkAns( isFlashCard ) || isFlashCard ) ? "numOfRight" : "numOfWrong" ]++;
stats.problem[ currIndex ].isCorrect = checkAns( isFlashCard );
displayAnsResult( isFlashCard );
$( ".q-next-btn", currQuiz ).attr( "disabled", false );
$( e.target ).attr( "disabled", true );
}
else{
setCheckBtnToBeBlockedUntilAnsSelIsClicked();
displayRetryMsg();
}
e.preventDefault();
}
);
$(".q-next-btn", currQuiz ).click( function(e){
nextMove();
$( e.target ).attr( "disabled", true );
e.preventDefault();
});
},
// setQuitBtn(): Provides a quit confirm action.
setQuitBtn = function(){
$( ".q-quitYes-btn", currQuiz ).click( function(){
quit = true;
nextMove();
});
$( ".q-quit-confirm", currQuiz ).click( function(){
$( ".q-quit-confirm", currQuiz ).hide(); //.q-quit-confirm calls both the quitYes and quitNo btns.
$( ".q-quit-btn", currQuiz ).css("display", "inline");
});
$( ".q-quit-btn", currQuiz ).click( function( e ){
$( e.target ).hide();
$( ".q-quit-confirm", currQuiz ).css("display", "inline");
});
},
setTotalQuesLen = function(){
var i = q.prop.length, len = 0, numOfQQ = settings.numOfQuizQues;
while( i-- ){
len += wordList[ q.prop[ i ] ].length;
}
totalQuesLen = ( !numOfQQ || isNaN( numOfQQ ) || numOfQQ > len ) ? len : numOfQQ;
stats.numOfQues = totalQuesLen;
},
// setQuizTypePos(): This stores the position of the beginning and end index of each quiz type.
// This allows for each quiz type to be pinpointed to in an array.
setQuizTypePos = function(){
var i = 0, sum = 0, len = 0;
while( q.prop[ i ] ){
len = wordList[ q.prop[ i ] ].length;
sum += len;
q.indexMax[ i ] = sum - 1;
q.indexMin[ i ] = sum - len;
i++;
}
},
// createQuizEl(): Creates new quiz element and sets default button behaviors.
createQuizEl = function(){
$( ".q-help-btn", currQuiz).toggle(
function(){ animateThis( $( ".q-help-info", currQuiz), 1 ); },
function(){ animateThis( $( ".q-help-info", currQuiz), 0 ); }
);
setTotalQuesLen();
setQuizTypePos();
setQuitBtn();
setCheckAndNextBtn();
if( settings.review ){ setReviewMenu(); }
$( stickyEl ).append( currQuiz );
},
// cont(): Checks whether the questions are done or if quit was called.
cont = function(){
var result = ( !quit && ( currIndex < totalQuesLen ) );
if( !result ){ quit = true; }
return result;
},
// animateType[]: Stores animation styles to animate an element.
animateType = [
function( el, on ){ el[ ( on ) ? "show" : "hide" ]( 0 );},
function( el, on, spd ){ el[ ( on ) ? "fadeIn" : "fadeOut" ]( spd );},
function( el, on, spd ){ el[ ( on ) ? "slideDown" : "slideUp" ]( spd );},
function( el, on, spd ){
el.animate( { "height": "toggle", "width": "toggle" }, spd );
}
],
// animateThis(): Calls animations on an element and/or push a callback function.
animateThis = function( el, isShown, func ){
if( isShown > -1 ){
animateType[ settings.fxType ]( el, isShown, settings.fxSpeed );
}
if( func ){
el.queue( function(){ func(); $(el).dequeue();});
}
if( isBadMSIE && (isShown > 0) ){
$(el).animate( { opacity: 1} ).css( "display", "block" );
}
},
// setReviewNav(): Adds events and functions to change the review questions being viewed.
// changeCurrIndex() is main point of focus.
setReviewNav = function(){
var qReviewOpt = ".q-review-index:visible option:",
qReviewOptVal = qReviewOpt + "selected",
changeCurrIndex = function( i ){
var max = $( qReviewOpt + "last", currQuiz).val(),
min = $( qReviewOpt + "first", currQuiz).val(),
isIndexValid = ( i <= max && i >= min );
if( isIndexValid ){
$( ".q-review-index", currQuiz).val( i );
currIndex = i;
changeProb(true);
displayAnsResult();
$( ".q-reviewBar > :disabled", currQuiz).attr( "disabled", false );
}
// if a limit was reached. Disable the corresponding arrow.
if( i == max || i == min ){
$( ".q-" + (( i == min )?"left":"right") + "Arrow", currQuiz ).attr( "disabled", true );
}
};
$( ".q-review-index", currQuiz).change(function(e){
changeCurrIndex( parseInt( $(e.target).val(), 10) );
});
// Note: The prev||next values of the options tag are used because the list might not be in numerical order.
$( ".q-review-arrows", currQuiz).click(function( e ){
var isLeft = !!e.target.className.match( /left/ ),
reviewVal = $( qReviewOptVal, currQuiz)[ ( isLeft ) ? "prev":"next" ]().val();
changeCurrIndex( parseInt( reviewVal, 10) );
});
},
// setReviewMenu(): Sets the events to change the view when the "review" or "details" button is clicked.
setReviewMenu = function(){
var qG = $( ".q-gameOver", currQuiz ), qP = $( ".q-prob", currQuiz );
$( ".q-reviewBar-btns", currQuiz ).click( function( e ){
var isDetailEl = ( e.target.className.search(/details/) > -1),
showEl = ( isDetailEl ) ? qG : qP,
hideEl = ( !isDetailEl ) ? qG : qP;
animateThis( $( ".q-reviewBar", currQuiz), !isDetailEl );
animateThis( hideEl, 0, function(){
$( ".q-details-btn", currQuiz ).attr( "disabled", isDetailEl );
$( ".q-review-btn", currQuiz ).attr( "disabled", !isDetailEl );
animateThis( showEl, 1 );
});
});
$( ".q-showOnlyMissed-btn", currQuiz ).change( function(){
var ckd = $(this).is(":checked");
if( ckd ){
$( ".q-review-index-missed", currQuiz ).fadeIn().css( "display", "inline" );
$( ".q-review-index-all", currQuiz ).hide();
}
else{
$( ".q-review-index-missed", currQuiz ).hide();
$( ".q-review-index-all", currQuiz ).fadeIn().css( "display", "inline" );
}
$( ".q-review-index:visible", currQuiz ).trigger( "change" );
});
setReviewNav();
},
// updateReviewIndex(): Creates the review's select tag html for all and wrong answers.
updateReviewIndex = function(){
var allMissed = '', optHtml = '', markAsWrong = '', eachOpt = '',
i = 0, totalQues = stats.totalQues;
while( totalQues - i ){
markAsWrong = ( getProblemProp( "isCorrect", i ) ) ? "" : " *";
eachOpt = "";
optHtml += eachOpt;
allMissed += (markAsWrong) ? eachOpt : "";
i++;
}
$( ".q-review-index-all", currQuiz).html( optHtml );
$( ".q-review-index-missed", currQuiz).html( allMissed );
},
// disableGameOverButtons(): If requested, on gameOver hide the restart and/or delete buttons.
disableGameOverButtons = function(){
if( settings.disableRestart ){ $( ".q-restart-btn", currQuiz).hide(); }
if( settings.disableDelete ){ $( ".q-del-btn", currQuiz).hide(); }
if( settings.disableRestart && settings.disableDelete ){
$( ".q-options", currQuiz ).hide();
}
},
getUserStatDetailsForDisplay = function(){
return [
(lang.stats.right + ": " + stats.numOfRight),
(lang.stats.wrong + ": " + stats.numOfWrong),
(lang.stats.total + ": " + stats.percTxt())
].join(' ');
},
setupReview = function(){
updateReviewIndex();
$( ".q-review-btn", currQuiz ).one( "click", function(){
$( ".q-review-index", currQuiz).trigger( "change" );
displayAnsResult();
// This forces the answer info to be displayed for first question.
if( totalQuesLen == 1 ){
$( ".q-rightArrow, .q-showOnlyMissed-btn", currQuiz ).attr( "disabled", true );
}
});
$( ".q-details-btn", currQuiz).attr( "disabled", true );
$( ".q-review-menu", currQuiz).show();
},
// gameOver(): Creates a performance list, set's the events for restart and delete, hides unneed elements,
// then enables the review-bar.
gameOver = function(){
disableGameOverButtons();
$( ".q-statTotal", currQuiz).html( lang.stats.score + ": " + stats.perc() + "%" );
$( ".q-statDetails", currQuiz).html( getUserStatDetailsForDisplay() );
$( ".q-restart-btn", currQuiz).one( "click", reStartQuiz );
$( ".q-del-btn", currQuiz).one( "click", deleteQuiz );
$( ".q-help, .q-check-btn, .q-prob, .q-intro, .q-ans", currQuiz).hide();
if( settings.review ){
setupReview();
}
animateThis( $( ".q-gameOver", currQuiz), 1, sendStatus );
},
// changeProb(): Changes to next problem and updates status.
changeProb = function( isReview ){
var qAS = q.ansSel;
$( ".q-counter", currQuiz).text( (currIndex + 1) + '/' + totalQuesLen );
$( ".q-ques", currQuiz).html( q.ques[ currIndex ] );
if( !isReview ){
$( ".q-ansSel", currQuiz).html(
// Checks for an index pointer; used to clone an identical q.ansSel. This was done to speed up buildQuizType.
qAS[ isNaN( qAS[currIndex] ) ? currIndex : qAS[currIndex] ].clone(true)
);
setQResultDisplay( 0 );
$( ".q-result", currQuiz ).hide();
$( ".focusThis", currQuiz ).focus();
setTimeout( function(){
$( ".clickThis", currQuiz ).trigger("click");
// IE BUG FIX. The list-style-type won't get rendered until hovered.
// Resetting the property will forces it to render. Runs slow though.
var qOlLiEl = $( ".q-ol-li", currQuiz );
if( qOlLiEl.length && isBadMSIE ){
qOlLiEl.css( "list-style-type", qOlLiEl.css( "list-style-type" ) );
}
}, 15);
}
},
setHelpBtn = function(){
if (settings.help) {
$(".q-help-info", currQuiz).html(settings.help);
} else {
$(".q-help-btn", currQuiz).attr("disabled", true);
}
},
// changeQuizInfo(): Changes the main information for the quiz.
changeQuizInfo = function(){
setToBeginningView();
$( ".q-title", currQuiz).html( settings.title );
setHelpBtn();
if( settings.intro ){
$( ".q-prob", currQuiz ).hide();
$( ".q-intro-info", currQuiz ).html( settings.intro );
$( ".q-intro", currQuiz ).show();
$( ".q-begin-btn", currQuiz ).unbind().one( "click", function(){
animateThis( $( ".q-intro", currQuiz ), 0, function(){
animateThis( $( ".q-prob", currQuiz ), 1, sendStatus );
});
});
}
},
goToNextProb = function( i ){
if( i && i < totalQuesLen && (currIndex + 1) < i ){
currIndex = i;
}
changeProb();
},
sendStatus = function(){
if( !settings.statusUpdate ){
return;
}
var quizInfo = {
"currIndex": currIndex,
"problem": stats.problem,
"numOfRight":stats.numOfRight,
"numOfWrong":stats.numOfWrong,
"score": stats.perc(),
"total": stats.totalQues,
"hasQuit": quit,
"deleteQuiz": deleteQuiz,
"quitQuiz": ( !quit ) ? quitQuiz : function(){},
"nextQuestion": ( !quit ) ? goToNextProb : function(){}
};
settings.statusUpdate( quizInfo, currQuiz );
},
// nextMove(): Decides to change the problem or to quit.
nextMove = function(){
currIndex++; // currIndex++ must be first, to find out if there are other questions.
var nextFunc = (cont() ? changeProb : quitQuiz),
qEl = $( ".q-prob", currQuiz);
if( !quit ){ animateThis( qEl, 0 ); }
animateThis( qEl, -1, nextFunc ); //Push the function to the stack, so it's called in between the animations.
if( !quit ){ animateThis( qEl, 1, sendStatus ); }
},
// getAns(): Is used to check the answers.
getAns = function( i ){
return q.ans[ i || currIndex ];
},
getStatsProblem = function( i ){
i = i || currIndex;
if( !stats.problem[ i ] ){
stats.problem[ i ] = { "amountTried": 0, "isCorrect":null, "userAnswer": undefined };
}
return stats.problem[ i ];
},
getProblemProp = function( prop, i ){
return getStatsProblem( i )[ prop ];
},
setUserAnswer = function( value, i ){
getStatsProblem( i ).userAnswer = value;
},
// checkAns(): Changes the user's answer during the quiz and updates the stats.
checkAns = function( isFlashCard ){
var ans = getAns(), isAnsCorr = false, userAns = getProblemProp( "userAnswer" );
if( userAns === undefined ){ return false; }
if( typeof(ans) === "string"){
ans = $.trim( ans );
}
if( isArray( ans ) ){
// If one element of the array matches the user's input, then it's correct.
// This enables multiple answers to be possible.
isAnsCorr = ( userAns == ans.toString() || ($.inArray( userAns, ans ) + 1));
}
else{
isAnsCorr = ( userAns == ans || userAns.toString().toLowerCase() == ans.toString().toLowerCase() );
}
return isAnsCorr;
},
getUserAnsForDisplay = function( toUni ){
var ans = lang.ans.yourAns + " ";
return ( !getProblemProp( "userAnswer" ) ) ? "" + ans +"" :
ans + getStrWithSeeableHTML( getProblemProp( "userAnswer" ), toUni );
},
getAnsInfoForDisplay = function(){
if( !q.ansInfo[ currIndex ] ){
return "";
}
if( q.ansSelInfo[ currIndex ] ){
return "" + lang.ans.whyAns + " " + (q.ansSelInfo[ currIndex ][ stats.indexSelected[ currIndex] ] || q.ansInfo[ currIndex ]);
}else{
return "" + lang.ans.whyAns + " " + (q.ansInfo[ currIndex ] );
}
},
//setQResultDisplay(): IE Bug Fix. .q-result pops out when hidden. Thus, show() -> append() and hide() -> remove().
setQResultDisplay = function( doAppend ){
if( !isBadMSIE ){ return; }
if( doAppend ){
if( !$( ".q-result", currQuiz ).size() ){
$(".q-prob", currQuiz ).append( $("").addClass( "q-result q-probArea" ).show() );
}
}
else{
$( ".q-result", currQuiz ).remove();
}
},
displayFeedback = function( str ){
setQResultDisplay(1);
$( ".q-result", currQuiz ).html( str );
if( !quit ){
animateThis( $( ".q-result", currQuiz ), 1 );
}
else{
$( ".q-result", currQuiz ).show();
}
},
// displayAnsResult(): Display's the answer result and information during the quiz.
displayAnsResult = function( isFlashCard ){
var currAns = getAns(), isUserAnsCorr = haveCorrAnsWithinRetries( isFlashCard ),
show = "", toUni = settings.showHTML;
currAns = ( isArray( currAns ) ) ? currAns.concat(): [ currAns ];
show = ( isUserAnsCorr ) ? lang.ans.praise :
( lang.ans.corrAns + ' ' + getStrArrOfSeeableHTML( currAns, toUni ).join( ' ' ) );
if( !isUserAnsCorr || quit ){
if( settings.showWrongAns || quit ){
show = getUserAnsForDisplay( toUni ) + "" + show;
}
if( settings.showAnsInfo ){
show += getAnsInfoForDisplay();
}
}else{
if( settings.alwaysShowAnsInfo ){
show += getAnsInfoForDisplay();
}
}
if( settings.showFeedback || quit ){
displayFeedback( show );
}
},
hasAnsSel = function( iProp, i ){
return ( !!wordList[ q.prop[ iProp ] ][ i ].ansSel );
},
rAnsSel = function( iProp, i ){
var wAS = wordList[ q.prop[ iProp ] ][ i ].ansSel,
j = rNum( ( isArray( wAS ) ) ? wAS.length : 0 );
return {
index: j,
value: ( isArray( wAS ) ) ? wAS[ j ] : wAS
};
},
releaseButtonBlock = function(){
if( settings.showFeedback ){
$( ".q-check-btn", currQuiz ).attr( "disabled", !$( ".q-next-btn", currQuiz ).attr("disabled") );
}
else{
$( ".q-next-btn", currQuiz ).attr( "disabled", false );
}
},
setAnsSelInfoSelect = function( quizType ){
if( !q.ansSelInfo[ currIndex ] ){
return;
}
groupClass = {
"multi":".q-select:visible > option",
"multiList":".q-ol-li:visible",
"tf":".userInputArea > input"
}[quizType];
selectedClass = {
"multi":".q-select > option:selected",
"multiList":"."+settings.activeClass,
"tf":".userInputArea > input:checked"
}[quizType];
stats.indexSelected[ currIndex ] = $( groupClass, currQuiz ).index( $(selectedClass, currQuiz ) );
},
// buildQuizType{}: contains all the quiz types.
//Quiz creation: The q object is filled with the questions, answers and answer selections(what the user can choose from).
//The user's input gets assigned to stats.problem[ currIndex ].userAnswer ( global inside quizMe ).
//Then nextMove() is called. Which checks to see if the user's input is contained in the answer, or is the array.
buildQuizType = {
// fillInTheBlank Quiz: ques = ques, ansSel = Input text[ ans ].
'fillInTheBlank' : function( iProp, flashVer ){
var d;
if( flashVer ){
d = $( "" ).addClass( "clickThis userInputArea" ) //Enabled the checkBtn.
.one( "click", function(){
releaseButtonBlock();
})
.bind( "getUserAns", function(){ return 1; });
}
else{
d = $( '' ).addClass( "q-quesInput focusThis userInputArea" )
.one( "click keypress", function(){
releaseButtonBlock();
})
.bind( "getUserAns", function(){
setUserAnswer( $.trim( $( ".q-quesInput", currQuiz ).val() ) );
});
}
var i = q.indexMax[ iProp ], qMin = q.indexMin[ iProp ], wList = wordList[ q.prop[ iProp ] ], numOfQues = wList.length;
while( numOfQues-- ){
q.retryCount[ i ] = wList[ numOfQues ].retry || 0;
q.ansInfo[ i ] = wList[ numOfQues ].ansInfo || "";
q.ans[ i ] = wList[ numOfQues ].ans;
q.ansSel[ i ] = qMin;
q.ques[ i ] = ( wList[ numOfQues ].ques + settings.addToEnd );
i--;
}
q.ansSel[ qMin ] = d; //speed!! All the q.ansSel area reference to qMin.
},
'flashCard' : function( iProp ){
// flashCard Quiz: ques = ques, ansSel =null .
this.fillInTheBlank( iProp, true);
},
'trueOrFalse' : function( iProp ){
// trueOrFalse Quiz:
// ques = (typeof ans != bool) ? (real or fake ans) : ques;
// ansSel = T / F ( radio );
// If the answer is a bool, then there is no creation of a question: combining a right or wrong answers, to make a true or false statement.
var d = $( ['