var validator; var appexDeleteMsg = ""; var mcqDeleteMsg = ""; // configure the wizard. Call after doing var validator = $("#templateForm").validate({ // screens need to specify the tabNumber for the save button if they are hiding tabs function initializeWizard(validatorIn, startValidationOnTab, tblVersion) { validator = validatorIn; if ( typeof startValidationOnTab === "undefined" ) startValidationOnTab = 0; $('#rootwizard').bootstrapWizard({ 'nextSelector': '.button-next', 'previousSelector': '.button-previous', 'onTabShow': function(tab, navigation, index) { var total = navigation.find('li:visible').length; var current = index+1; var percent = (current/total) * 100; $('#rootwizard .progress-bar').css({width:percent+'%'}) if ( navigation.find('li:last').get(0) == tab.get(0) ) { $('.button-save').show(); $('.button-next').hide(); } else { $('.button-next').show(); $('.button-save').hide(); } }, 'tabClass': 'nav nav-pills', 'onNext': function(tab, navigation, index) { if ( index > (startValidationOnTab + 1) ) { var valid = $("#templateForm").valid(); if(!valid) { validator.focusInvalid(); return false; } } } }); } // Remove the display:none or the fields won't be validate as jquery validation is set to only valid non hidden fields. // If we allow validation of hidden fields then we cannot have validation on the "next" button as it uses the hidden fields // to avoid validating the fields on other tabs. Still should not be seen as visibility is hidden. Also need to catch // the editor update and redo validation otherwise the "field blah is blank" error message won't go away when the user enters text. function reconfigureCKEditorInstance(instance) { $('#'+instance.name).css('display','inline'); instance.on('blur', function() { $('#'+instance.name).valid(); }); } function refreshCKEditors() { // make sure all the ckeditors are refreshed, not just the validated ones for (var i in CKEDITOR.instances) { CKEDITOR.instances[i].updateElement(); } } function validateCK(textarea) { CKEDITOR.instances[textarea.id].updateElement(); // update textarea var editorcontent = textarea.value.replace(/<[^>]*>/gi, ''); // strip tags return editorcontent.length === 0; } function templateInvalidHandler(e, validator) { if (validator.numberOfInvalids()) { $("#templateErrorMessages").show(); } else { $("#templateErrorMessages").hide(); } } function submitForm(form) { $('#error-message').empty(); refreshCKEditors(); var jqxhr = $.ajax( { method: $(form).attr('method'), url: getSubmissionURL() + '.do', data: $(form).serialize() }) .done(function(data) { var learningDesignID = data.learningDesignID; if ( ! learningDesignID) { var errors = data.errors; if ( errors ) { $('#templateErrorMessages').show(); $('#error-message').show(); for (var i = 0; i < errors.length; i++) { $('#error-message').append('
  • '+errors[i]+'
  • '); } } else { var fatal = data.fatal; if ( fatal ) { alert(fatal); } else { alert('Save failed (expected parameters missing). Data returned by server was '+data); } } $('.button-save').button('reset'); } else { var title = encodeURIComponent(data.title); location.href='authoring/template/createresult.jsp?learningDesignID='+learningDesignID +'&learningDesigntitle='+title; } }) .fail(function() { alert('Save failed. Please see the server logs for more details.\n\n'); $('.button-save').button('reset'); }); } function getSubmissionURL() { return 'authoring/template/'+$('#template').val().toLowerCase(); } function doGotoList() { if(confirm("")){ location.href = 'authoring/template/list.jsp'; } } function doCancel() { if(confirm("")){ closeWindow(); } } // Called by save button function doSaveForm() { $('.button-save').button('loading'); $('#templateForm').submit(); } // Triggers the import window. The saving is done in a method saveQTI(formHTML, formName, callerID) which should be defined in the main template jsp file. // CallerID can be set to define which tab has triggered the QTI import, as TBL has import on both the RAT Questions and App Ex tabs. function importQTI(callerID, limit, type){ var url = 'questions/questionFile.jsp?collectionChoice=false&callerID='+callerID; if ( limit ) { url = url + '&limitType='+limit; } if ( type ) { url = url + '&importType='+type; } // open import pop up window, centered horizontally var left = ((screen.width / 2) - (500 / 2)); window.open(url,'QuestionFile','width=500,height=370,scrollbars=yes,top=150,left=' + left); } function createAssessment(questionType, numAssessmentsFieldname, containingDivName, qbQuestionUid, collapse) { var numAssessments = $('#'+numAssessmentsFieldname); var type = questionType ? questionType : 'essay'; var currNum = numAssessments.val(); var nextNum = +currNum + 1; var newDiv = document.createElement("div"); newDiv.id = containingDivName+'divassess'+nextNum; newDiv.className = 'space-top space-sides'; var url=getSubmissionURL()+"/createAssessment.do?questionNumber="+nextNum+"&questionType="+type+"&containingDivName="+containingDivName; if (qbQuestionUid) { url += '&qbQuestionUid=' + qbQuestionUid; } $('#'+containingDivName).append(newDiv); $.ajaxSetup({ cache: true }); $(newDiv).load(url, function( response, status, xhr ) { if ( status == "error" ) { console.log( xhr.status + " " + xhr.statusText ); newDiv.remove(); } else { numAssessments.val(nextNum); if (collapse) { $('.collapse', newDiv).collapse('hide'); } else { newDiv.scrollIntoView(); } } }); } function createQuestion(numQuestionsFieldname, newDivPrefix, questionDivName, forward, extraParam, collapse) { var numQuestions = $('#'+numQuestionsFieldname); var currNum = numQuestions.val(); var nextNum = +currNum + 1; var newDiv = document.createElement("div"); newDiv.id = newDivPrefix+nextNum; var url=getSubmissionURL()+"/createQuestion.do?questionNumber="+nextNum; if ( forward && forward.length > 0) { url = url + "&forward=" + forward; } if ( extraParam ) { url = url + extraParam; } $('#'+questionDivName).append(newDiv); $.ajaxSetup({ cache: true }); $(newDiv).load(url, function( response, status, xhr ) { if ( status == "error" ) { console.log( xhr.status + " " + xhr.statusText ); newDiv.remove(); } else { numQuestions.val(nextNum); if (collapse) { $('.collapse', newDiv).collapse('hide'); } else { newDiv.focus(); newDiv.scrollIntoView(); } } }); } function createOption(questionNum, maxOptionCount) { var currNum = $('#numOptionsQuestion'+questionNum).val(); var nextNum = +currNum + 1; var newDiv = document.createElement("div"); newDiv.id = 'divq'+questionNum+'opt'+nextNum; var optionsDiv=$('#divq'+questionNum+'options'); var lastChild=optionsDiv.children().filter(':last'); $(lastChild).after(newDiv); var url=getSubmissionURL()+"/createOption.do?questionNumber="+questionNum+"&optionNumber="+nextNum; $.ajaxSetup({ cache: true }); $(newDiv).load(url, function( response, status, xhr ) { if ( status == "error" ) { console.log( xhr.status + " " + xhr.statusText ); newDiv.remove(); } else { $('#numOptionsQuestion'+questionNum).val(nextNum); if ( nextNum >= maxOptionCount ) { $('#createOptionButton'+questionNum).hide(); } // need to add the down button to the previous last option! var image = document.getElementById('question'+questionNum+'option'+currNum+'DownButton') image.style.display="inline"; newDiv.scrollIntoView(); } }); } function createAssessmentOption(questionNum, maxOptionCount, containingDivName) { var currNum = $('#'+containingDivName+'assmcq'+questionNum+'numOptions').val(); var nextNum = +currNum + 1; var newDiv = document.createElement("div"); newDiv.id = containingDivName+'divassmcq'+questionNum+'opt'+nextNum; var optionsDiv=$('#'+containingDivName+'divassmcq'+questionNum+'options'); var lastChild=optionsDiv.children().filter(':last'); $(lastChild).after(newDiv); var url=getSubmissionURL()+"/createOption.do?questionNumber="+questionNum+"&optionNumber="+nextNum+"&assess=true&containingDivName="+containingDivName; $.ajaxSetup({ cache: true }); $(newDiv).load(url, function( response, status, xhr ) { if ( status == "error" ) { console.log( xhr.status + " " + xhr.statusText ); newDiv.remove(); } else { $('#'+containingDivName+'assmcq'+questionNum+'numOptions').val(nextNum); if ( nextNum >= maxOptionCount ) { $('#'+containingDivName+'createAssessmentOptionButton'+questionNum).hide(); } // need to add the down button to the previous last option! var image = document.getElementById(containingDivName+'assmcq'+questionNum+'option'+currNum+'DownButton') image.style.display="inline"; newDiv.scrollIntoView(); } }); } function getOptionData(questionNum, paramPrefix) { var data = { }; var correctField = paramPrefix + "correct"; $('#templateForm').find('input, textarea, select').each(function() { if ( this.name == correctField ) { if ( this.checked ) { data[this.name] = $(this).val(); } } else if ( this.name.startsWith(paramPrefix) ) { data[this.name] = $(this).val(); } }); return data; } function swapOptions(questionNum, optionNum1, optionNum2, divToLoad, appexContainingDivName) { refreshCKEditors() ; var paramPrefix = appexContainingDivName ? appexContainingDivName + "assmcq" : "question"; paramPrefix = paramPrefix + questionNum; var data = getOptionData(questionNum, paramPrefix); var url=getSubmissionURL()+"/swapOption.do?questionNumber="+questionNum+"&optionNumber1="+optionNum1 +"&optionNumber2="+optionNum2; if ( appexContainingDivName ) { url += "&containingDivName="+appexContainingDivName+"&assess=true"; } $.ajaxSetup({ cache: true }); jqueryDivToLoad = divToLoad ? $('#'+divToLoad) : $('#divq'+questionNum+'options'); jqueryDivToLoad.load(url, data, function( response, status, xhr ) { if ( status == "error" ) { alert("Swap failed. See server logs for details. "+xhr.statusText); console.log( xhr.status + " " + xhr.statusText ); } }); } function removeOption(questionNum, optionNum, divToLoad, appexContainingDivName) { refreshCKEditors() ; var paramPrefix = appexContainingDivName ? appexContainingDivName + "assmcq" : "question"; paramPrefix = paramPrefix + questionNum; var data = getOptionData(questionNum, paramPrefix); var url=getSubmissionURL()+"/deleteOption.do?questionNumber="+questionNum+"&optionNumber="+optionNum; if ( appexContainingDivName ) url += "&containingDivName="+appexContainingDivName+"&assess=true"; $.ajaxSetup({ cache: true }); jqueryDivToLoad = divToLoad ? $('#'+divToLoad) : $('#divq'+questionNum+'options'); jqueryDivToLoad.load(url, data, function( response, status, xhr ) { if ( status == "error" ) { alert("Delete failed. See server logs for details. "+xhr.statusText); console.log( xhr.status + " " + xhr.statusText ); } else { $('#createOptionButton'+questionNum).show(); } }); } function createForum(numTopicsFieldname, newDivPrefix, forumDivName, forward) { var numTopics = $('#'+numTopicsFieldname); var currNum = numTopics.val(); var nextNum = +currNum + 1; var newDiv = document.createElement("div"); newDiv.id = newDivPrefix+nextNum; var url=getSubmissionURL()+"/createForum.do?topicNumber="+nextNum; if ( forward && forward.length > 0) { url = url + "&forward=" + forward; } $('#'+forumDivName).append(newDiv); $.ajaxSetup({ cache: true }); $(newDiv).load(url, function( response, status, xhr ) { if ( status == "error" ) { console.log( xhr.status + " " + xhr.statusText ); newDiv.remove(); } else { numTopics.val(nextNum); newDiv.focus(); newDiv.scrollIntoView(); } }); } function createURL(numURLsFieldname, newDivPrefix, urlDivName, extraParam) { var numUrls = $('#'+numURLsFieldname); var currNumURLS = numUrls.val(); var nextNumURLS = +currNumURLS + 1; var urlDiv = document.createElement("div"); urlDiv.id = newDivPrefix+nextNumURLS; var url=getSubmissionURL()+"/createResource.do?urlNumber="+nextNumURLS; if ( extraParam ) { url = url + extraParam; } $('#'+urlDivName).append(urlDiv); $.ajaxSetup({ cache: true }); $(urlDiv).load(url, function( response, status, xhr ) { if ( status == "error" ) { console.log( xhr.status + " " + xhr.statusText ); urlDiv.remove(); } else { numUrls.val(nextNumURLS); } }); } function createBranch() { var currNum = $('#numberOfBranches').val(); var nextNum = +currNum + 1; var branchDiv = document.createElement("div"); branchDiv.id = 'divbranch'+nextNum; var url=getSubmissionURL()+"/createBranch.do?branchNumber="+nextNum; $('#divbranches').append(branchDiv); $.ajaxSetup({ cache: true }); $(branchDiv).load(url, function( response, status, xhr ) { if ( status == "error" ) { console.log( xhr.status + " " + xhr.statusText ); $('#divurls').remove(); } else { $('#numberOfBranches').val(nextNum); } }); } function createPeerReviewCriteria() { var currNum = $('#numRatingCriterias').val(); var nextNum = +currNum + 1; var newDiv = document.createElement("div"); newDiv.id = 'divrating'+nextNum; newDiv.className = 'space-top'; var url=getSubmissionURL()+"/createRatingCriteria.do?criteriaNumber="+nextNum; $('#divratings').append(newDiv); $.ajaxSetup({ cache: true }); $(newDiv).load(url, function( response, status, xhr ) { if ( status == "error" ) { console.log( xhr.status + " " + xhr.statusText ); newDiv.remove(); } else { $('#numRatingCriterias').val(nextNum); newDiv.scrollIntoView(); } }); } function deleteAppexDiv(idOfDivToDelete, idOfTitleField) { if ( confirm(appexDeleteMsg.replace("{0}", "\""+$("#"+idOfTitleField).val()+"\"")) ) { $("#"+idOfDivToDelete).remove(); } } function deleteQuestionDiv(idOfDivToDelete, idOfTitleField) { if ( confirm(mcqDeleteMsg.replace("{0}", "\""+$("#"+idOfTitleField).val()+"\"")) ) { $("#"+idOfDivToDelete).remove(); } } function testURL(urlField) { launchPopup($('#'+urlField).val(),'popupUrl'); } function validateNoSpecialCharacters(inputText) { if ( inputText ) { var validator = /^[^<>^*@%$]*$/igm; var result = validator.test(inputText); return result; } return true; } // Functions used of x-editable. // Shown as a fudge the validator to keep x-editable compatible with jquery validator function onShownForXEditable(e, editable) { $(this).nextAll('i.fa-pencil').hide(); var $innerForm = $(this).data('editable').input.$input.closest('form'); var $outerForm = $innerForm.parents('form').eq(0); $innerForm.data('validator', $outerForm.data('validator')); } function onHiddenForXEditable(e, reason) { $(this).nextAll('i.fa-pencil').show(); } function validateXEditable(value) { //close editing area on validation failure if (!value.trim()) { $('.editable-open').editableContainer('hide', 'cancel'); return 'Can not be empty!'; } }