// 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-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 = $( ['
'].join('') ); $( d ).addClass( "userInputArea" ).bind( "getUserAns", function(){ var isTrue = $( ".true-radio", this ).attr( "checked" ); setUserAnswer( lang.quiz[ ( isTrue ) ? "tfTrue" : "tfFalse" ] ); setAnsSelInfoSelect( "tf" ); }); $( d ).children().one( "click", function(){ releaseButtonBlock(); }); $(".trueRadio", d).click( function(){ $( ".true-radio", currQuiz ).attr( "checked", true ); $( ".false-radio", currQuiz ).attr( "checked", false ); }); $(".falseRadio", d).click( function(){ $( ".true-radio", currQuiz ).attr( "checked", false ); $( ".false-radio", currQuiz ).attr( "checked", true ); }); var currAns, shouldAnsBeCorr = true, i = q.indexMax[ iProp ], qMin = q.indexMin[ iProp ], wList = wordList[ q.prop[ iProp ] ], numOfQues = wList.length, toUni = settings.showHTML, tfEqual = lang.quiz.tfEqual, tfEnd = lang.quiz.tfEnd, rAS, cAS, result, isAnsReallyCorrect; while( numOfQues-- ){ rAS = undefined; cAS = undefined; currAns = wList[ numOfQues ].ans; q.ansInfo[ i ] = wList[ numOfQues ].ansInfo || ""; q.ansSel[ i ] = qMin; q.retryCount[ i ] = wList[ numOfQues ].retry || 0; q.ques[ i ] = wList[ numOfQues ].ques; if( typeof currAns != "boolean" ){ shouldAnsBeCorr = rBool(); if( shouldAnsBeCorr ){ result = currAns; }else{ if( hasAnsSel( iProp, numOfQues ) ){ rAS = rAnsSel( iProp, numOfQues ); result = rAS.value || currAns; } else{ result = rAns(iProp); } } isAnsReallyCorrect = ( !isArray( currAns ) || !isArray( result ) ) ? result == currAns : areArraysSame( result, currAns ); q.ans[ i ] = lang.quiz[ ( isAnsReallyCorrect ) ? "tfTrue" : "tfFalse" ]; q.ques[ i ] += tfEqual; q.ques[ i ] += getStrWithSeeableHTML( ( isArray( result ) ) ? result.join( ',' ) : result, toUni ); q.ques[ i ] += tfEnd; } else{ q.ans[ i ] = lang.quiz[ ( currAns ) ? "tfTrue" : "tfFalse" ]; } if( wList[ numOfQues ].ansSelInfo ){ cAS = wList[ numOfQues ].ansSelInfo.concat(); if( cAS && rAS ){ cAS = ( isArray( cAS ) ) ? cAS : [ cAS ]; var qAI = q.ansInfo[ i ]; q.ansSelInfo[ i ] = ( shouldAnsBeCorr ) ? [ qAI, cAS[ rAS.index ] || qAI ] : [ cAS[ rAS.index ] || qAI, q.ansInfo[ i ] ]; } } i--; } q.ansSel[ qMin ] = d; }, 'multipleChoice' : function( iProp, olVer ){ // multipleChoice Quiz: // ques = ques; // ansSel =
' ); $( ".q-select", d ).one( "click keypress", function( e ){ releaseButtonBlock(); }); } $( d ).addClass( "userInputArea" ).bind( "getUserAns", function(){ if( olVer ){ var elHtml = $("." + settings.activeClass, currQuiz).find(".q-ol-li-ansSel")[ settings.showHTML ? "text": "html" ](); setUserAnswer( $.trim(elHtml) ); setAnsSelInfoSelect( "multiList" ); } else{ setUserAnswer( $( "option:selected", this ).text() ); setAnsSelInfoSelect( "multi" ); } }); var i = q.indexMax[ iProp ], wList = wordList[ q.prop[ iProp ] ], ansPos, optHtml, val, len, j = wList.length, wListRange = getNumRange( j ), numOfQues = wList.length, //Checks to see, if the request multiLen length can be supported. setLen = ( settings.multiLen > j ) ? j : settings.multiLen, toUni = settings.showHTML, hasAnsPosition = false, ansSelChoice = "", range, cASI, cProb, ansIndex, x; var addClassFunc = function( e ){ $( e.target ).closest( "li.q-ol-li" ).addClass( settings.hoverClass ); }, removeClassFunc = function( e ){ $( e.target ).closest( "li.q-ol-li" ).removeClass( settings.hoverClass ); }, clickFunc = function( e ){ var qOL = $(e.target).closest("li.q-ol-li"); qOL.addClass(settings.activeClass).siblings().removeClass(settings.activeClass).find(".q-radioBtn:checked").attr("checked", false); qOL.find(".q-radioBtn").attr("checked", true); }; while (numOfQues--) { cProb = wList[numOfQues]; optHtml = ""; hasAnsPosition = false; cASI = cProb.ansSelInfo; cASI = ( cASI ) ? ( ( isArray( cASI ) ) ? cASI.concat() : [ cASI ] ) : undefined; if (hasAnsSel(iProp, numOfQues)) { val = []; if ( isArray( cProb.ansSel ) ) { var cAS = cProb.ansSel, k = cAS.length; while ( k-- ) { if (cAS[ k ] === null) { val[ k ] = cProb.ans; if( cASI ){ cASI.splice( k, 1, cProb.ansInfo ); } hasAnsPosition = true; } else { val[ k ] = cAS[k]; } } } else { val[0] = cProb.ansSel; } if (!hasAnsPosition) { val[val.length] = cProb.ans; if( cASI ){ cASI[ cASI.length ] = cProb.ansInfo; } } len = val.length; range = getNumRange( len ); if( !hasAnsPosition ){ makeArrayRandom( range ); if( cASI ){ cASI = getArrayOfSwappedArrayElements( cASI, range ); } } while (len--) { ansSelChoice = getStrWithSeeableHTML( val[ range[ len ] ], toUni ); if( cASI ){ cASI[ len ] = getStrWithSeeableHTML( cASI[ len ], toUni ); } x = (olVer) ? ["
  • ", ansSelChoice, "
  • "].join("") : [" "].join(""); optHtml = x + optHtml; } } else { var randAns = wListRange.concat(); // Get rids of the answer Index from randAns. randAns.splice(numOfQues, 1); len = setLen; ansPos = rNum(len); while (len--) { var randIndex = rNum(randAns.length), ansSelIndex = (len == ansPos) ? numOfQues: (randAns.splice(randIndex, 1) || 0); ansSelChoice = getStrWithSeeableHTML(wList[ansSelIndex].ans, toUni); x = (olVer) ? ["
  • ", ansSelChoice, "
  • "].join("") : [" "].join(""); optHtml = x + optHtml; } } // below: innerHTML is 2x faster than $.html() but IE generates corrrupt option html if (isBadMSIE && !olVer) { $("select", d).html(optHtml); } else { $((olVer) ? ".q-ol": "select", d)[0].innerHTML = optHtml; } if (isBadMSIE && olVer) { //Fixes IE CSS BUG. No spaces with list-style-position: inline. $(".q-ol-li", d).prepend(" "); } if( olVer ){ $( "li.q-ol-li", d ).bind( "click", clickFunc ) .bind( "mouseover", addClassFunc ) .bind( "mouseout", removeClassFunc ); } q.retryCount[i] = cProb.retry || 0; q.ans[i] = cProb.ans; q.ansInfo[i] = cProb.ansInfo || ""; q.ansSel[i] = d.clone(true); q.ansSelInfo[ i ] = ( cASI ) ? cASI.concat() : cASI; q.ques[i] = cProb.ques; i--; } }, 'multipleChoiceOl' : function( iProp ){ // multipleChoiceOlQuiz: ques = ques, ansSel = ol [ real ans + * settings.multipleChooseLength] this.multipleChoice( iProp, true); } }, // makeQuizRandom(): If randomizeQ is false, it will randomize the each quiz type. // But if randomizeQ is true, it will randomize the q object. makeQuizRandom = function( randomizeQ ){ if( randomizeQ ){ var numArr = getNumRange( totalQuesLen ), i = numArr.length, temp = { ans: [], ansSel: [], ansInfo: [], ques: [] }, nArrI, qAS; makeArrayRandom( numArr ); i = numArr.length; while( i-- ){ nArrI = numArr[ i ]; qAS = q.ansSel[ nArrI ]; temp.ans[i] = q.ans[ nArrI ]; temp.ansInfo[i] = q.ansInfo[ nArrI ] || ""; temp.ansSel[i] = ( typeof qAS !== "number" ) ? qAS : q.ansSel[ qAS ]; temp.ques[i] = q.ques[ nArrI ]; } q.ans = temp.ans.concat(); q.ansInfo = temp.ansInfo.concat(); q.ansSel = temp.ansSel.concat(); q.ques = temp.ques.concat(); } else{ var iProp = q.prop.length; while( iProp-- ){ makeArrayRandom( wordList[ q.prop[ iProp ] ] ); } } }, makeQuizType = function(){ //iProp is a pointer to the quizType min and max index. var iProp = q.prop.length, currProp = "", p, allP = settings.allQuizType, shortProp = { fill:"fillInTheBlank", cards:"flashCard", tf:"trueOrFalse", multi:"multipleChoice", multiList:"multipleChoiceOl" }; if( settings.random ){ makeQuizRandom();} while( iProp-- ){ p = allP || q.prop[ iProp ]; currProp = shortProp[ p ] || p; buildQuizType[ currProp ]( iProp ); } if( settings.allRandom ){ makeQuizRandom(true); } }, // Creates and shows the new quiz. createNewQuizProb = function(){ stats.reset(); makeQuizType(); changeQuizInfo(); changeProb(); animateThis( $( currQuiz ), 1, sendStatus ); }, quitQuiz = function(){ currIndex = 0; //Used to make updateReviewIndex start at 0. quit = true; disableMenuBar(); gameOver(); }, deleteQuiz = function(){ animateThis( $( currQuiz ), 0, function(){ $( currQuiz ).remove(); }); q = {}; }, setToBeginningView = function(){ $( ".q-gameOver, .q-result, .q-review-menu", currQuiz).hide(); $( ".q-ans, .q-help, .q-help-menu, .q-prob", currQuiz ).show(); $( ".q-check-btn", currQuiz )[ ( settings.showFeedback)?"show":"hide"]().attr( "disabled", true ); $( ".q-next-btn", currQuiz ).attr( "disabled", settings.showFeedback ); $( ".q-quit-btn, .q-help-btn", currQuiz ).attr( "disabled", false ); }, reStartQuiz = function(){ //IE likes to hide on restart. if( !isBadMSIE || ( isBadMSIE && settings.fxType == 3 ) ){ animateThis( $( currQuiz ), 0 ); } setToBeginningView(); quit = false; currIndex = 0; createNewQuizProb(); }, setAnimation = function( s ){ var spd = s.fxSpeed, spdType = { slow: 600, fast: 200, normal: 400 }; s.fxSpeed = isNaN( spd ) ? ( spdType[ spd ] || 400 ) : spd; if( s.fxCode ){ var custAnimatePos = animateType.length; animateType[ custAnimatePos ] = s.fxCode; s.fxType = custAnimatePos; } }, checkAnsSelInfo = function( ansInfo, ansSel, ansSelInfo ){ var a = isArray( ansSel ), b = isArray( ansSelInfo ); if( ansInfo === undefined ){ return lang.err.ansInfoNotDefined; } if( ( a || b ) && (a != b || ansSel.length !== ansSelInfo.length)){ return lang.err.badAnsSelInfoLen; } }, // checkQTypeKeys(): Checks the keys for each question, wordList[i]. Returns error if unknown. checkQTypeKeys = function( quesKeys ){ var badKey = "", i = quesKeys.length, temp, validKeys = { ques:1, ans:1, ansInfo:1, ansSel:1, ansSelInfo:1, retry:1 }; if( !i ){ badKey = lang.err.noQues; } while( i-- ){ if( badKey ){ break;} temp = quesKeys[ i ]; for( var currKey in temp ){ if( temp.hasOwnProperty( currKey ) ){ if( !validKeys[ currKey ] ){ badKey = currKey + lang.err.badKey; break; } if( currKey == "ansSelInfo" ){ badKey = checkAnsSelInfo( temp.ansInfo || undefined, temp.ansSel || undefined, temp.ansSelInfo ); break; } } } } return badKey; }, hasBadSyntax = function( wNames ){ var badKey = "", goodKeys = [], validKeys = { fillInTheBlank:1, flashCard:1, trueOrFalse:1, multipleChoice:1, multipleChoiceOl:1, fill:1, cards:1, tf:1, multi:1, multiList:1 }; for( var prop in wNames ){ if( wNames.hasOwnProperty( prop ) ){ if( !validKeys[ prop ] ){ badKey = lang.err[ ( typeof wNames === "object" ) ? "badQType" : "lang.err.notObj" ]; } if( !badKey ){ badKey = ( isArray( wNames[prop] ) ) ? checkQTypeKeys( wNames[prop] ) : lang.err.notArr; } if( badKey ){ badKey = prop + " -> " + badKey; break; } } } if( !prop ){ badKey = lang.err.noQType; } if( !badKey && settings.allQuizType && !validKeys[ settings.allQuizType ] ){ badKey = settings.allQuizType + " -> " + lang.err.badQType; } return badKey; }, checkForErrors = function( obj ){ var err = ( obj ) ? hasBadSyntax( obj ) : lang.err.noQues; if( err ){ err = "jQuizMe " + lang.err.error + ": " + err; $( stickyEl ).text( err ); throw new Error( err ); } }, convertArrToObj = function( obj ){ var tempObj = {}; tempObj[ settings.allQuizType || settings.quizType ] = obj; return tempObj; }, // start(): This is the first function called. start = function(){ wordList = ( isArray( wordList ) ) ? convertArrToObj( wordList ) : wordList; if( settings.performErrorChecking ){ checkForErrors( wordList ); } setAnimation( settings ); q.prop = getObjProps( wordList ); createQuizEl(); createNewQuizProb(); return currQuiz; }(); // Auto starts. }); }; $.fn.jQuizMe.version = "2.2"; })(jQuery);