PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / trunk
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses vtrunk
4.4.7 4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 4.4.0 4.3.9.1 4.3.9 4.3.8 4.3.7 4.1.6.9 4.1.6.9.1 4.1.6.9.2 4.1.6.9.3 4.1.6.9.4 4.1.7 4.1.7.1 4.1.7.2 4.1.7.3 4.1.7.3.1 4.1.7.3.2 4.2.0 4.2.1 All 138 releases
learnpress / assets / src / js / admin / edit-question.js

edit-question.js in LearnPress – WordPress LMS Plugin for Create and Sell Online Courses trunk, at assets/src/js/admin/edit-question.js

1,559 lines 42.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Edit question JS handler.
3 *
4 * @since 4.2.9
5 * @version 1.0.0
6 */
7
8 import * as lpUtils from 'lpAssetsJsPath/utils.js';
9 import * as lpToastify from 'lpAssetsJsPath/lpToastify';
10 import SweetAlert from 'sweetalert2';
11 import Sortable from 'sortablejs';
12
13 const idUrlHandle = 'edit-question';
14 let fibSelection;
15 let timeoutAutoUpdateAnswer, timeoutAutoUpdateFib, timeoutAutoUpdateQuestion;
16
17 // EditQuestion class
18 export class EditQuestion {
19 static selectors = {
20 elEditQuestionWrap: '.lp-edit-question-wrap',
21 elQuestionEditMain: '.lp-question-edit-main',
22 elQuestionToggleAll: '.lp-question-toggle-all',
23 elEditListQuestions: '.lp-edit-list-questions',
24 elQuestionToggle: '.lp-question-toggle',
25 elBtnShowPopupItemsToSelect: '.lp-btn-show-popup-items-to-select',
26 elPopupItemsToSelectClone: '.lp-popup-items-to-select.clone',
27 elBtnAddQuestion: '.lp-btn-add-question',
28 elBtnRemoveQuestion: '.lp-btn-remove-question',
29 elBtnUpdateQuestionTitle: '.lp-btn-update-question-title',
30 elBtnUpdateQuestionDes: '.lp-btn-update-question-des',
31 elBtnUpdateQuestionHint: '.lp-btn-update-question-hint',
32 elBtnUpdateQuestionExplain: '.lp-btn-update-question-explanation',
33 elQuestionTitleNewInput: '.lp-question-title-new-input',
34 elQuestionTitleInput: '.lp-question-title-input',
35 elQuestionTypeLabel: '.lp-question-type-label',
36 elQuestionTypeNew: '.lp-question-type-new',
37 elAddNewQuestion: 'add-new-question',
38 elQuestionClone: '.lp-question-item.clone',
39 elAnswersConfig: '.lp-answers-config',
40 elBtnAddAnswer: '.lp-btn-add-question-answer',
41 elQuestionAnswerItemAddNew: '.lp-question-answer-item-add-new',
42 elQuestionAnswerTitleNewInput: '.lp-question-answer-title-new-input',
43 elQuestionAnswerTitleInput: '.lp-question-answer-title-input',
44 elBtnDeleteAnswer: '.lp-btn-delete-question-answer',
45 elQuestionByType: '.lp-question-by-type',
46 elInputAnswerSetTrue: '.lp-input-answer-set-true',
47 elQuestionAnswerItem: '.lp-question-answer-item',
48 elBtnUpdateQuestionAnswer: '.lp-btn-update-question-answer',
49 elBtnFibInsertBlank: '.lp-btn-fib-insert-blank',
50 elBtnFibDeleteAllBlanks: '.lp-btn-fib-delete-all-blanks',
51 elBtnFibSaveContent: '.lp-btn-fib-save-content',
52 elBtnFibClearAllContent: '.lp-btn-fib-clear-all-content',
53 elFibOptionTitleInput: '.lp-question-fib-option-title-input',
54 elFibBlankOptions: '.lp-question-fib-blank-options',
55 elFibBlankOptionItem: '.lp-question-fib-blank-option-item',
56 elFibBlankOptionItemClone: '.lp-question-fib-blank-option-item.clone',
57 elFibBlankOptionIndex: '.lp-question-fib-option-index',
58 elBtnFibOptionDelete: '.lp-btn-fib-option-delete',
59 elFibOptionMatchCaseWrap: '.lp-question-fib-option-match-case-wrap',
60 elFibOptionMatchCaseInput: '.lp-question-fib-option-match-case-input',
61 elQuestionFibOptionDetail: '.lp-question-fib-option-detail',
62 elFibOptionComparisonInput: '.lp-question-fib-option-comparison-input',
63 elAutoSaveFib: '.lp-auto-save-fib',
64 LPTarget: '.lp-target',
65 elCollapse: 'lp-collapse',
66 elSectionToggle: '.lp-section-toggle',
67 elTriggerToggle: '.lp-trigger-toggle',
68 elAutoSaveQuestion: '.lp-auto-save-question',
69 elAutoSaveAnswer: '.lp-auto-save-question-answer',
70 elQuestionFibInput: 'lp-question-fib-input',
71 elBtnQuestionCreateType: '.lp-btn-question-create-type',
72 };
73
74 constructor() {}
75
76 init() {
77 this.events();
78 this.initTinyMCE().then();
79 }
80
81 events() {
82 if ( EditQuestion._loadedEvents ) {
83 return;
84 }
85 EditQuestion._loadedEvents = true;
86
87 // Sortable answers's question
88 const elQuestionEditMains = document.querySelectorAll(
89 `${ EditQuestion.selectors.elQuestionEditMain }`
90 );
91 elQuestionEditMains.forEach( ( elQuestionEditMain ) => {
92 this.sortAbleQuestionAnswer( elQuestionEditMain );
93 } );
94 // End sortable
95
96 // Event click
97 lpUtils.eventHandlers( 'click', [
98 {
99 selector: EditQuestion.selectors.elBtnQuestionCreateType,
100 callBack: this.createQuestionType.name,
101 class: this,
102 },
103 {
104 selector: EditQuestion.selectors.elBtnAddAnswer,
105 callBack: this.addQuestionAnswer.name,
106 class: this,
107 },
108 {
109 selector: EditQuestion.selectors.elBtnDeleteAnswer,
110 callBack: this.deleteQuestionAnswer.name,
111 class: this,
112 },
113 {
114 selector: EditQuestion.selectors.elBtnFibInsertBlank,
115 callBack: this.fibInsertBlank.name,
116 class: this,
117 },
118 {
119 selector: EditQuestion.selectors.elBtnFibDeleteAllBlanks,
120 callBack: this.fibDeleteAllBlanks.name,
121 class: this,
122 },
123 {
124 selector: EditQuestion.selectors.elBtnFibSaveContent,
125 callBack: this.fibSaveContent.name,
126 class: this,
127 },
128 {
129 selector: EditQuestion.selectors.elBtnFibClearAllContent,
130 callBack: this.fibClearContent.name,
131 class: this,
132 },
133 {
134 selector: EditQuestion.selectors.elBtnFibOptionDelete,
135 callBack: this.fibDeleteBlank.name,
136 class: this,
137 },
138 {
139 selector: EditQuestion.selectors.elFibOptionMatchCaseInput,
140 callBack: this.fibShowHideMatchCaseOption.name,
141 class: this,
142 },
143 {
144 selector: EditQuestion.selectors.elFibOptionComparisonInput,
145 callBack: ( args ) => {
146 const { e, target } = args;
147 const elQuestionEditMain = target.closest(
148 `${ EditQuestion.selectors.elQuestionEditMain }`
149 );
150
151 const elBtnFibSaveContent =
152 elQuestionEditMain.querySelector(
153 `${ EditQuestion.selectors.elBtnFibSaveContent }`
154 );
155
156 elBtnFibSaveContent.click();
157 },
158 },
159 ] );
160
161 // Toggle collapse
162 document.addEventListener( 'click', ( e ) => {
163 const target = e.target;
164 lpUtils.toggleCollapse(
165 e,
166 target,
167 EditQuestion.selectors.elTriggerToggle
168 );
169 } );
170
171 // Event keyup
172 lpUtils.eventHandlers( 'keyup', [
173 {
174 selector: EditQuestion.selectors.elQuestionAnswerTitleNewInput,
175 callBack: this.checkCanAddAnswer.name,
176 class: this,
177 },
178 {
179 selector: EditQuestion.selectors.elFibOptionTitleInput,
180 callBack: this.fibOptionTitleInputChange.name,
181 class: this,
182 },
183 {
184 selector: EditQuestion.selectors.elAutoSaveQuestion,
185 callBack: this.autoUpdateQuestion.name,
186 class: this,
187 },
188 ] );
189
190 // Event keydown
191 lpUtils.eventHandlers( 'keydown', [
192 {
193 selector: EditQuestion.selectors.elQuestionAnswerTitleNewInput,
194 callBack: this.addQuestionAnswer.name,
195 class: this,
196 checkIsEventEnter: true,
197 },
198 ] );
199
200 // Event change
201 lpUtils.eventHandlers( 'change', [
202 {
203 selector: EditQuestion.selectors.elAutoSaveAnswer,
204 callBack: this.autoUpdateAnswer.name,
205 class: this,
206 },
207 ] );
208
209 // TinyMCE events
210 this.eventEditorTinymce();
211 }
212
213 // Run async to re-init all TinyMCE editors, because it slow if have many editors
214 async initTinyMCE() {
215 const elTextareas = document.querySelectorAll( '.lp-editor-tinymce' );
216
217 elTextareas.forEach( ( elTextarea ) => {
218 const idTextarea = elTextarea.id;
219
220 this.reInitTinymce( idTextarea );
221 } );
222 }
223
224 reInitTinymce( id ) {
225 window.tinymce.execCommand( 'mceRemoveEditor', true, id );
226 window.tinymce.execCommand( 'mceAddEditor', true, id );
227 }
228
229 reInitQuickTags( id ) {
230 const toolbar = document.getElementById( `qt_${ id }_toolbar` );
231 if ( ! toolbar || toolbar.children.length || ! window.quicktags ) {
232 return;
233 }
234
235 const settings = window.tinyMCEPreInit?.qtInit?.[ id ] || { id };
236 window.quicktags( settings );
237
238 if ( window.QTags?._buttonsInit ) {
239 window.QTags._buttonsInit();
240 }
241 }
242
243 setDefaultEditorTab( id ) {
244 const wrapEditor = document.getElementById( `wp-${ id }-wrap` );
245 if ( ! wrapEditor ) {
246 return;
247 }
248
249 if ( wrapEditor.classList.contains( 'html-active' ) && window.switchEditors?.go ) {
250 window.switchEditors.go( id, 'tmce' );
251
252 const elTextarea = document.getElementById( id );
253 if ( elTextarea ) {
254 elTextarea.style.visibility = '';
255 }
256 }
257
258 wrapEditor.classList.add( 'tmce-active' );
259 wrapEditor.classList.remove( 'html-active' );
260
261 const visualTab = document.getElementById( `${ id }-tmce` );
262 const codeTab = document.getElementById( `${ id }-html` );
263 visualTab?.setAttribute( 'aria-pressed', 'true' );
264 codeTab?.setAttribute( 'aria-pressed', 'false' );
265 }
266
267 // Events for TinyMCE editor
268 eventEditorTinymce() {
269 window.tinymce.on( 'AddEditor', ( eEditor ) => {
270 const id = eEditor.editor.id;
271 const editor = window.tinymce.get( id );
272 if ( ! editor ) {
273 return;
274 }
275
276 if ( id === 'content' ) {
277 return;
278 }
279
280 const elTextarea = document.getElementById( id );
281 if ( ! elTextarea ) {
282 return;
283 }
284
285 const elQuestionEditMain = elTextarea.closest(
286 `${ EditQuestion.selectors.elQuestionEditMain }`
287 );
288
289 // Skip if not in question edit context
290 if ( ! elQuestionEditMain ) {
291 return;
292 }
293
294 const questionId = elQuestionEditMain.dataset.questionId;
295 editor.settings.force_p_newlines = false;
296 editor.settings.forced_root_block = '';
297 editor.settings.force_br_newlines = true;
298
299 // Config use absolute url
300 editor.settings.relative_urls = false;
301 editor.settings.remove_script_host = false;
302 editor.settings.convert_urls = true;
303 editor.settings.document_base_url = lpData.site_url;
304 // End config use absolute url
305
306 // Add quick tags
307 this.reInitQuickTags( id );
308
309 // Events focus in TinyMCE editor
310 editor.on( 'change keyup', ( e ) => {
311 // Auto save if it has class lp-auto-save
312 elTextarea.value = editor.getContent();
313 this.autoUpdateQuestion( {
314 e,
315 target: elTextarea,
316 } );
317 } );
318
319 editor.on( 'blur', ( e ) => {
320 //console.log( 'Editor blurred:', e.target.id );
321 } );
322 editor.on( 'focusin', ( e ) => {} );
323 editor.on( 'init', () => {
324 // Add style
325 editor.dom.addStyle( `
326 body {
327 line-height: 2.2 !important;
328 }
329 .${ EditQuestion.selectors.elQuestionFibInput } {
330 border: 1px dashed rebeccapurple;
331 padding: 5px;
332 }
333 ` );
334
335 // Set default tab visual
336 this.setDefaultEditorTab( id );
337 } );
338 editor.on( 'setcontent', ( e ) => {
339 const uniquid = this.randomString();
340 const elementg = editor.dom.select(
341 `.${ EditQuestion.selectors.elQuestionFibInput }[data-id="${ uniquid }"]`
342 );
343 if ( elementg[ 0 ] ) {
344 elementg[ 0 ].focus();
345 }
346
347 editor.dom.bind( elementg[ 0 ], 'input', ( e ) => {
348 //console.log( 'Input changed:', e.target.value );
349 } );
350 } );
351 editor.on( 'selectionchange', ( e ) => {
352 fibSelection = editor.selection;
353
354 // Check selection is blank, check empty blank content
355 if (
356 fibSelection
357 .getNode()
358 .classList.contains(
359 `${ EditQuestion.selectors.elQuestionFibInput }`
360 )
361 ) {
362 const blankId = fibSelection.getNode().dataset.id;
363 const textBlank = fibSelection.getNode().textContent.trim();
364 if ( textBlank.length === 0 ) {
365 const editorId = editor.id;
366 const questionId = editorId.replace(
367 `${ EditQuestion.selectors.elQuestionFibInput }-`,
368 ''
369 );
370 const elQuestionEditMain = document.querySelector(
371 `${ EditQuestion.selectors.elQuestionEditMain }[data-question-id="${ questionId }"]`
372 );
373 const elQuestionBlankOptions =
374 elQuestionEditMain.querySelector(
375 `${ EditQuestion.selectors.elFibBlankOptions }`
376 );
377 const elFibBlankOptionItem =
378 elQuestionBlankOptions.querySelector(
379 `${ EditQuestion.selectors.elFibBlankOptionItem }[data-id="${ blankId }"]`
380 );
381 if ( elFibBlankOptionItem ) {
382 lpUtils.lpShowHideEl( elFibBlankOptionItem, 0 );
383 }
384 } else {
385 const elTextarea = document.getElementById( id );
386 const elAnswersConfig = elTextarea.closest(
387 `${ EditQuestion.selectors.elAnswersConfig }`
388 );
389 const elFibBlankOptionItem =
390 elAnswersConfig.querySelector(
391 `${ EditQuestion.selectors.elFibBlankOptionItem }[data-id="${ blankId }"]`
392 );
393 if ( elFibBlankOptionItem ) {
394 const elFibOptionTitleInput =
395 elFibBlankOptionItem.querySelector(
396 `${ EditQuestion.selectors.elFibOptionTitleInput }`
397 );
398 if ( elFibOptionTitleInput ) {
399 elFibOptionTitleInput.value = textBlank;
400 }
401 }
402 }
403 }
404 } );
405 editor.on( 'Undo', ( e ) => {
406 const contentUndo = editor.getContent();
407 const selection = editor.selection;
408 const nodeUndo = selection.getNode();
409
410 if (
411 nodeUndo.classList.contains(
412 `${ EditQuestion.selectors.elQuestionFibInput }`
413 )
414 ) {
415 const blankId = nodeUndo.dataset.id;
416 const elFibBlankOptionItem = document.querySelector(
417 `${ EditQuestion.selectors.elFibBlankOptionItem }[data-id="${ blankId }"]`
418 );
419
420 if ( elFibBlankOptionItem ) {
421 lpUtils.lpShowHideEl( elFibBlankOptionItem, 1 );
422 }
423 }
424 } );
425 editor.on( 'Redo', ( e ) => {} );
426 } );
427 }
428
429 autoUpdateQuestion( args ) {
430 let { e, target, key, value } = args;
431 const elAutoSave = target.closest(
432 `${ EditQuestion.selectors.elAutoSaveQuestion }`
433 );
434 if ( ! elAutoSave ) {
435 return;
436 }
437
438 const elQuestionEditMain = elAutoSave.closest(
439 `${ EditQuestion.selectors.elQuestionEditMain }`
440 );
441 const questionId = elQuestionEditMain.dataset.questionId;
442
443 clearTimeout( timeoutAutoUpdateQuestion );
444 timeoutAutoUpdateQuestion = setTimeout( () => {
445 // Call ajax to update question description
446 const callBack = {
447 success: ( response ) => {
448 const { message, status } = response;
449
450 if ( status === 'success' ) {
451 lpToastify.show( message, status );
452 } else {
453 throw `Error: ${ message }`;
454 }
455 },
456 error: ( error ) => {
457 lpToastify.show( error, 'error' );
458 },
459 completed: () => {},
460 };
461
462 const dataSend = {
463 action: 'update_question',
464 question_id: questionId,
465 args: {
466 id_url: idUrlHandle,
467 },
468 };
469
470 if ( undefined === key ) {
471 key = elAutoSave.dataset.keyAutoSave;
472 if ( ! key ) {
473 if (
474 ! elAutoSave.classList.contains( 'lp-editor-tinymce' )
475 ) {
476 return;
477 }
478
479 const textAreaId = elAutoSave.id;
480 key = textAreaId
481 .replace( /lp-/g, '' )
482 .replace( `-${ questionId }`, '' )
483 .replace( /-/g, '_' );
484 if ( ! key ) {
485 return;
486 }
487 }
488
489 value = elAutoSave.value;
490 }
491
492 dataSend[ key ] = value;
493
494 window.lpAJAXG.fetchAJAX( dataSend, callBack );
495 }, 700 );
496 }
497 // Create question type
498 createQuestionType( args ) {
499 const { e, target } = args;
500 const elBtnQuestionCreateType = target.closest(
501 `${ EditQuestion.selectors.elBtnQuestionCreateType }`
502 );
503 if ( ! elBtnQuestionCreateType ) {
504 return;
505 }
506
507 const elQuestionEditMain = elBtnQuestionCreateType.closest(
508 `${ EditQuestion.selectors.elQuestionEditMain }`
509 );
510 if ( ! elQuestionEditMain ) {
511 return;
512 }
513
514 const questionId = elQuestionEditMain.dataset.questionId;
515 const elQuestionTypeNew = elQuestionEditMain.querySelector(
516 `${ EditQuestion.selectors.elQuestionTypeNew }`
517 );
518 if ( ! elQuestionTypeNew ) {
519 return;
520 }
521
522 const questionType = elQuestionTypeNew.value.trim();
523 if ( ! questionType ) {
524 const message = elQuestionTypeNew.dataset.messEmptyType;
525 lpToastify.show( message, 'error' );
526 return;
527 }
528
529 // Call ajax to create new question type
530 const callBack = {
531 success: ( response ) => {
532 const { message, status, data } = response;
533
534 if ( status === 'success' ) {
535 const { html_option_answers } = data;
536 const elAnswersConfig = elQuestionEditMain.querySelector(
537 `${ EditQuestion.selectors.elAnswersConfig }`
538 );
539 elAnswersConfig.outerHTML = html_option_answers;
540 this.initTinyMCE();
541 this.sortAbleQuestionAnswer( elQuestionEditMain );
542
543 lpToastify.show( message, status );
544 } else {
545 throw `Error: ${ message }`;
546 }
547 },
548 error: ( error ) => {
549 lpToastify.show( error, 'error' );
550 },
551 completed: () => {},
552 };
553
554 const dataSend = {
555 action: 'update_question',
556 question_id: questionId,
557 question_type: questionType,
558 args: {
559 id_url: idUrlHandle,
560 },
561 };
562 window.lpAJAXG.fetchAJAX( dataSend, callBack );
563 }
564
565 addQuestionAnswer( args ) {
566 const { e, target } = args;
567 const elQuestionAnswerItemAddNew = target.closest(
568 `${ EditQuestion.selectors.elQuestionAnswerItemAddNew }`
569 );
570 if ( ! elQuestionAnswerItemAddNew ) {
571 return;
572 }
573
574 e.preventDefault();
575
576 const elQuestionAnswerTitleNewInput =
577 elQuestionAnswerItemAddNew.querySelector(
578 `${ EditQuestion.selectors.elQuestionAnswerTitleNewInput }`
579 );
580
581 if ( ! elQuestionAnswerTitleNewInput.value.trim() ) {
582 const message =
583 elQuestionAnswerTitleNewInput.dataset.messEmptyTitle;
584 lpToastify.show( message, 'error' );
585 return;
586 }
587
588 const elQuestionEditMain = target.closest(
589 `${ EditQuestion.selectors.elQuestionEditMain }`
590 );
591 const elQuestionAnswerClone = elQuestionEditMain.querySelector(
592 `${ EditQuestion.selectors.elQuestionAnswerItem }.clone`
593 );
594 const elQuestionAnswerNew = elQuestionAnswerClone.cloneNode( true );
595 const elQuestionAnswerTitleInputNew = elQuestionAnswerNew.querySelector(
596 `${ EditQuestion.selectors.elQuestionAnswerTitleInput }`
597 );
598
599 elQuestionAnswerNew.classList.remove( 'clone' );
600 lpUtils.lpShowHideEl( elQuestionAnswerNew, 1 );
601 lpUtils.lpSetLoadingEl( elQuestionAnswerNew, 1 );
602 elQuestionAnswerClone.insertAdjacentElement(
603 'beforebegin',
604 elQuestionAnswerNew
605 );
606
607 const answerTitle = elQuestionAnswerTitleNewInput.value.trim();
608 elQuestionAnswerTitleInputNew.value = answerTitle;
609 elQuestionAnswerTitleNewInput.value = '';
610 const questionId = elQuestionEditMain.dataset.questionId;
611
612 // Call ajax to add new question answer
613 const callBack = {
614 success: ( response ) => {
615 const { message, status, data } = response;
616
617 if ( status === 'success' ) {
618 const { question_answer } = data;
619 elQuestionAnswerNew.dataset.answerId =
620 question_answer.question_answer_id;
621 lpUtils.lpSetLoadingEl( elQuestionAnswerNew, 0 );
622
623 // Set data lp-answers-config
624 const dataAnswers =
625 this.getDataAnswersConfig( elQuestionEditMain );
626 dataAnswers.push( question_answer );
627 this.setDataAnswersConfig(
628 elQuestionEditMain,
629 dataAnswers
630 );
631 } else {
632 throw `Error: ${ message }`;
633 }
634
635 lpToastify.show( message, status );
636 },
637 error: ( error ) => {
638 elQuestionAnswerNew.remove();
639 lpToastify.show( error, 'error' );
640 },
641 completed: () => {},
642 };
643
644 const dataSend = {
645 action: 'add_question_answer',
646 question_id: questionId,
647 answer_title: answerTitle,
648 args: {
649 id_url: idUrlHandle,
650 },
651 };
652 window.lpAJAXG.fetchAJAX( dataSend, callBack );
653 }
654
655 // Check to enable or disable add new question button
656 checkCanAddAnswer( args ) {
657 const { e, target } = args;
658 const elTrigger = target.closest(
659 EditQuestion.selectors.elQuestionAnswerTitleNewInput
660 );
661 if ( ! elTrigger ) {
662 return;
663 }
664
665 const elQuestionAnswerItemAddNew = elTrigger.closest(
666 `${ EditQuestion.selectors.elQuestionAnswerItemAddNew }`
667 );
668 if ( ! elQuestionAnswerItemAddNew ) {
669 return;
670 }
671
672 const elBtnAddAnswer = elQuestionAnswerItemAddNew.querySelector(
673 `${ EditQuestion.selectors.elBtnAddAnswer }`
674 );
675 if ( ! elBtnAddAnswer ) {
676 return;
677 }
678
679 const titleValue = elTrigger.value.trim();
680 if ( titleValue ) {
681 elBtnAddAnswer.classList.add( 'active' );
682 } else {
683 elBtnAddAnswer.classList.remove( 'active' );
684 }
685 }
686
687 // Auto update question answer
688 autoUpdateAnswer( args ) {
689 const { e, target } = args;
690 const elAutoSaveAnswer = target.closest(
691 `${ EditQuestion.selectors.elAutoSaveAnswer }`
692 );
693 if ( ! elAutoSaveAnswer ) {
694 return;
695 }
696
697 const elQuestionAnswerItem = elAutoSaveAnswer.closest(
698 `${ EditQuestion.selectors.elQuestionAnswerItem }`
699 );
700
701 clearTimeout( timeoutAutoUpdateAnswer );
702 timeoutAutoUpdateAnswer = setTimeout( () => {
703 const elQuestionEditMain = elAutoSaveAnswer.closest(
704 `${ EditQuestion.selectors.elQuestionEditMain }`
705 );
706
707 const questionId = elQuestionEditMain.dataset.questionId;
708 const dataAnswers = this.getDataAnswersConfig( elQuestionEditMain );
709 const elAnswersConfig = elQuestionEditMain.querySelector(
710 `${ EditQuestion.selectors.elAnswersConfig }`
711 );
712
713 // For both radio and checkbox.
714 const dataAnswersOld = structuredClone( dataAnswers );
715
716 // Get position of answers
717 const elQuestionAnswerItems = elAnswersConfig.querySelectorAll(
718 `${ EditQuestion.selectors.elQuestionAnswerItem }:not(.clone)`
719 );
720 const answersPosition = {};
721 elQuestionAnswerItems.forEach( ( elQuestionAnswerItem, index ) => {
722 answersPosition[ elQuestionAnswerItem.dataset.answerId ] =
723 index + 1; // Start from 1
724 } );
725
726 //console.log( 'answersPosition', answersPosition );
727
728 dataAnswers.map( ( answer, k ) => {
729 const elQuestionAnswerItem = elQuestionEditMain.querySelector(
730 `${ EditQuestion.selectors.elQuestionAnswerItem }[data-answer-id="${ answer.question_answer_id }"]`
731 );
732 const elInputAnswerSetTrue = elQuestionAnswerItem.querySelector(
733 `${ EditQuestion.selectors.elInputAnswerSetTrue }`
734 );
735 const elInputAnswerTitle = elQuestionAnswerItem.querySelector(
736 `${ EditQuestion.selectors.elQuestionAnswerTitleInput }`
737 );
738
739 // Set title
740 if ( elInputAnswerTitle ) {
741 answer.title = elInputAnswerTitle.value.trim();
742 }
743
744 // Set true answer
745 if ( elInputAnswerSetTrue ) {
746 if ( elInputAnswerSetTrue.checked ) {
747 answer.is_true = 'yes';
748 } else {
749 answer.is_true = '';
750 }
751 }
752
753 // Set position
754 if ( answersPosition[ answer.question_answer_id ] ) {
755 answer.order = answersPosition[ answer.question_answer_id ];
756 }
757
758 return answer;
759 } );
760
761 //console.log( dataAnswers );
762
763 lpUtils.lpSetLoadingEl( elQuestionAnswerItem, 1 );
764
765 // Call ajax to update answers config
766 const callBack = {
767 success: ( response ) => {
768 const { message, status } = response;
769
770 if ( status === 'success' ) {
771 } else {
772 throw `Error: ${ message }`;
773 }
774
775 lpToastify.show( message, status );
776 },
777 error: ( error ) => {
778 // rollback changes to old data
779 dataAnswersOld.forEach( ( answer ) => {
780 const elAnswerItem = elQuestionEditMain.querySelector(
781 `${ EditQuestion.selectors.elQuestionAnswerItem }[data-answer-id="${ answer.question_answer_id }"]`
782 );
783 const inputAnswerSetTrue = elAnswerItem.querySelector(
784 `${ EditQuestion.selectors.elInputAnswerSetTrue }`
785 );
786 if ( answer.is_true === 'yes' ) {
787 inputAnswerSetTrue.checked = true;
788 }
789
790 return answer;
791 } );
792 lpToastify.show( error, 'error' );
793 },
794 completed: () => {
795 lpUtils.lpSetLoadingEl( elQuestionAnswerItem, 0 );
796 },
797 };
798
799 const dataSend = {
800 action: 'update_question_answers_config',
801 question_id: questionId,
802 answers: dataAnswers,
803 args: {
804 id_url: idUrlHandle,
805 },
806 };
807 window.lpAJAXG.fetchAJAX( dataSend, callBack );
808 }, 700 );
809 }
810
811 // Sortable answers's question
812 sortAbleQuestionAnswer( elQuestionEditMain ) {
813 let isUpdateSectionPosition = 0;
814 let timeout;
815
816 const elQuestionAnswers = elQuestionEditMain.querySelectorAll(
817 `${ EditQuestion.selectors.elAnswersConfig }`
818 );
819
820 elQuestionAnswers.forEach( ( elAnswersConfig ) => {
821 new Sortable( elAnswersConfig, {
822 handle: '.drag',
823 animation: 150,
824 onEnd: ( evt ) => {
825 const elQuestionAnswerItem = evt.item;
826 if ( ! isUpdateSectionPosition ) {
827 // No change in section position, do nothing
828 return;
829 }
830
831 clearTimeout( timeout );
832 timeout = setTimeout( () => {
833 const elAutoSaveAnswer =
834 elQuestionAnswerItem.querySelector(
835 `${ EditQuestion.selectors.elAutoSaveAnswer }`
836 );
837 this.autoUpdateAnswer( {
838 e: null,
839 target: elAutoSaveAnswer,
840 } );
841 }, 1000 );
842 },
843 onMove: ( evt ) => {
844 clearTimeout( timeout );
845 },
846 onUpdate: ( evt ) => {
847 isUpdateSectionPosition = 1;
848 },
849 } );
850 } );
851 }
852
853 // Delete question answer
854 deleteQuestionAnswer( args ) {
855 const { e, target } = args;
856 const elBtnDeleteAnswer = target.closest(
857 `${ EditQuestion.selectors.elBtnDeleteAnswer }`
858 );
859 if ( ! elBtnDeleteAnswer ) {
860 return;
861 }
862
863 const elQuestionAnswerItem = elBtnDeleteAnswer.closest(
864 `${ EditQuestion.selectors.elQuestionAnswerItem }`
865 );
866 if ( ! elQuestionAnswerItem ) {
867 return;
868 }
869
870 const elQuestionEditMain = elBtnDeleteAnswer.closest(
871 `${ EditQuestion.selectors.elQuestionEditMain }`
872 );
873
874 const questionId = elQuestionEditMain.dataset.questionId;
875 const questionAnswerId = elQuestionAnswerItem.dataset.answerId;
876 if ( ! questionId || ! questionAnswerId ) {
877 return;
878 }
879
880 SweetAlert.fire( {
881 title: elBtnDeleteAnswer.dataset.title || 'Are you sure?',
882 text:
883 elBtnDeleteAnswer.dataset.content ||
884 'Do you want to delete this answer?',
885 icon: 'warning',
886 showCloseButton: true,
887 showCancelButton: true,
888 cancelButtonText: lpData.i18n.cancel,
889 confirmButtonText: lpData.i18n.yes,
890 reverseButtons: true,
891 } ).then( ( result ) => {
892 if ( result.isConfirmed ) {
893 lpUtils.lpSetLoadingEl( elQuestionAnswerItem, 1 );
894
895 // Call ajax to delete item from section
896 const callBack = {
897 success: ( response ) => {
898 const { message, status } = response;
899
900 lpToastify.show( message, status );
901
902 if ( status === 'success' ) {
903 const elQuestionAnswerId = parseInt(
904 elQuestionAnswerItem.dataset.answerId
905 );
906 elQuestionAnswerItem.remove();
907
908 const dataAnswers =
909 this.getDataAnswersConfig( elQuestionEditMain );
910 if ( dataAnswers ) {
911 const updatedAnswers = dataAnswers.filter(
912 ( answer ) =>
913 parseInt(
914 answer.question_answer_id
915 ) !== elQuestionAnswerId
916 );
917 this.setDataAnswersConfig(
918 elQuestionEditMain,
919 updatedAnswers
920 );
921 }
922 }
923 },
924 error: ( error ) => {
925 lpToastify.show( error, 'error' );
926 },
927 completed: () => {
928 lpUtils.lpSetLoadingEl( elQuestionAnswerItem, 0 );
929 },
930 };
931
932 const dataSend = {
933 action: 'delete_question_answer',
934 question_id: questionId,
935 question_answer_id: questionAnswerId,
936 args: {
937 id_url: idUrlHandle,
938 },
939 };
940 window.lpAJAXG.fetchAJAX( dataSend, callBack );
941 }
942 } );
943 }
944
945 // Get data answers config
946 getDataAnswersConfig( elQuestionEditMain ) {
947 const elAnswersConfig = elQuestionEditMain.querySelector(
948 `${ EditQuestion.selectors.elAnswersConfig }`
949 );
950 if ( ! elAnswersConfig ) {
951 return null;
952 }
953
954 let dataAnswers = elAnswersConfig.dataset.answers || '[]';
955 try {
956 dataAnswers = JSON.parse( dataAnswers );
957 } catch ( e ) {
958 dataAnswers = [];
959 }
960
961 if ( ! dataAnswers.meta_data ) {
962 dataAnswers.meta_data = {};
963 }
964
965 return dataAnswers;
966 }
967
968 // Set data answers config
969 setDataAnswersConfig( elQuestionEditMain, dataAnswers ) {
970 const elAnswersConfig = elQuestionEditMain.querySelector(
971 `${ EditQuestion.selectors.elAnswersConfig }`
972 );
973 if ( ! elAnswersConfig ) {
974 return;
975 }
976
977 if ( ! dataAnswers || typeof dataAnswers !== 'object' ) {
978 dataAnswers = {};
979 }
980
981 elAnswersConfig.dataset.answers = JSON.stringify( dataAnswers );
982 }
983
984 /***** Fill in the blank question type *****/
985 // For FIB question type
986 fibInsertBlank = ( args ) => {
987 const { e, target } = args;
988 const elBtnFibInsertBlank = target.closest(
989 EditQuestion.selectors.elBtnFibInsertBlank
990 );
991 if ( ! elBtnFibInsertBlank ) {
992 return;
993 }
994
995 const textPlaceholder = elBtnFibInsertBlank.dataset.defaultText;
996 const elQuestionEditMain = elBtnFibInsertBlank.closest(
997 EditQuestion.selectors.elQuestionEditMain
998 );
999 const questionId = elQuestionEditMain.dataset.questionId;
1000 const messErrInserted = elBtnFibInsertBlank.dataset.messInserted;
1001 const messErrRequireSelectText =
1002 elBtnFibInsertBlank.dataset.messRequireSelectText;
1003 const idEditor = `${ EditQuestion.selectors.elQuestionFibInput }-${ questionId }`;
1004
1005 const uniquid = this.randomString();
1006 let selectedText;
1007 if ( fibSelection ) {
1008 const elNode = fibSelection.getNode();
1009 if ( ! elNode ) {
1010 lpToastify.show(
1011 'Event insert blank has error, please try again',
1012 'error'
1013 );
1014 return;
1015 }
1016
1017 const findParent = elNode.closest(
1018 `body[data-id="${ idEditor }"]`
1019 );
1020 if ( ! findParent ) {
1021 lpToastify.show( messErrRequireSelectText, 'error' );
1022 return;
1023 }
1024
1025 if (
1026 elNode.classList.contains(
1027 `${ EditQuestion.selectors.elQuestionFibInput }`
1028 )
1029 ) {
1030 lpToastify.show( messErrInserted, 'error' );
1031 return;
1032 }
1033
1034 selectedText = fibSelection.getContent();
1035 if ( selectedText.length === 0 ) {
1036 selectedText = textPlaceholder;
1037 }
1038
1039 const elInputNew = `<span class="${ EditQuestion.selectors.elQuestionFibInput }" data-id="${ uniquid }">${ selectedText }</span>`;
1040
1041 fibSelection.setContent( elInputNew );
1042 } else {
1043 lpToastify.show( messErrRequireSelectText, 'error' );
1044 return;
1045 }
1046
1047 const dataAnswers = this.getDataAnswersConfig( elQuestionEditMain );
1048 dataAnswers.meta_data = dataAnswers.meta_data || {};
1049 // Convert array to object
1050 if ( Object.keys( dataAnswers.meta_data ).length === 0 ) {
1051 dataAnswers.meta_data = {};
1052 }
1053
1054 dataAnswers.meta_data[ uniquid ] = {
1055 id: uniquid,
1056 match_case: 0,
1057 comparison: 'equal',
1058 fill: selectedText,
1059 index: 1,
1060 open: false,
1061 };
1062
1063 this.setDataAnswersConfig( elQuestionEditMain, dataAnswers );
1064
1065 // Clone blank options
1066 const elFibBlankOptions = elQuestionEditMain.querySelector(
1067 `${ EditQuestion.selectors.elFibBlankOptions }`
1068 );
1069 const elFibBlankOptionItemClone = elQuestionEditMain.querySelector(
1070 `${ EditQuestion.selectors.elFibBlankOptionItemClone }`
1071 );
1072 const elFibBlankOptionItemNew =
1073 elFibBlankOptionItemClone.cloneNode( true );
1074 const countOptions = elFibBlankOptions.querySelectorAll(
1075 `${ EditQuestion.selectors.elFibBlankOptionItem }:not(.clone)`
1076 ).length;
1077 const elFibBlankOptionIndex = elFibBlankOptionItemNew.querySelector(
1078 `${ EditQuestion.selectors.elFibBlankOptionIndex }`
1079 );
1080 const elFibOptionTitleInput = elFibBlankOptionItemNew.querySelector(
1081 `${ EditQuestion.selectors.elFibOptionTitleInput }`
1082 );
1083 const elFibOptionMatchCaseInput = elFibBlankOptionItemNew.querySelector(
1084 `${ EditQuestion.selectors.elFibOptionMatchCaseInput }`
1085 );
1086 const elFibOptionComparisonInput =
1087 elFibBlankOptionItemNew.querySelectorAll(
1088 `${ EditQuestion.selectors.elFibOptionComparisonInput }`
1089 );
1090
1091 elFibBlankOptionItemNew.dataset.id = uniquid;
1092 elFibOptionTitleInput.name = `${ EditQuestion.selectors.elFibOptionTitleInput }-${ uniquid }`;
1093 elFibOptionTitleInput.value = this.decodeHtml( selectedText );
1094 elFibBlankOptionIndex.textContent = countOptions + 1 + '.';
1095 elFibOptionMatchCaseInput.name =
1096 `${ EditQuestion.selectors.elFibOptionMatchCaseInput }-${ uniquid }`.replace(
1097 /\./g,
1098 ''
1099 );
1100 elFibOptionComparisonInput.forEach( ( elInput ) => {
1101 elInput.name =
1102 `${ EditQuestion.selectors.elFibOptionComparisonInput }-${ uniquid }`.replace(
1103 /\./g,
1104 ''
1105 );
1106 if ( elInput.value === 'equal' ) {
1107 elInput.checked = true;
1108 }
1109 } );
1110 elFibBlankOptionItemClone.insertAdjacentElement(
1111 'beforebegin',
1112 elFibBlankOptionItemNew
1113 );
1114 elFibBlankOptionItemNew.classList.remove( 'clone' );
1115 lpUtils.lpShowHideEl( elFibBlankOptionItemNew, 1 );
1116 // End clone blank options
1117
1118 const elBtnFibSaveContent = elQuestionEditMain.querySelector(
1119 `${ EditQuestion.selectors.elBtnFibSaveContent }`
1120 );
1121 lpUtils.lpSetLoadingEl( elBtnFibInsertBlank, 1 );
1122 this.fibSaveContent( {
1123 e: null,
1124 target: elBtnFibSaveContent,
1125 callBackCompleted: () => {
1126 lpUtils.lpSetLoadingEl( elBtnFibInsertBlank, 0 );
1127 },
1128 } );
1129 };
1130
1131 // Delete all blanks
1132 fibDeleteAllBlanks( args ) {
1133 const { e, target } = args;
1134 const elBtnFibDeleteAllBlanks = target.closest(
1135 `${ EditQuestion.selectors.elBtnFibDeleteAllBlanks }`
1136 );
1137 if ( ! elBtnFibDeleteAllBlanks ) {
1138 return;
1139 }
1140
1141 const elQuestionEditMain = elBtnFibDeleteAllBlanks.closest(
1142 `${ EditQuestion.selectors.elQuestionEditMain }`
1143 );
1144 if ( ! elQuestionEditMain ) {
1145 return;
1146 }
1147
1148 const questionId = elQuestionEditMain.dataset.questionId;
1149 const dataAnswers = this.getDataAnswersConfig( elQuestionEditMain );
1150
1151 SweetAlert.fire( {
1152 title: elBtnFibDeleteAllBlanks.dataset.title,
1153 text: elBtnFibDeleteAllBlanks.dataset.content,
1154 icon: 'warning',
1155 showCloseButton: true,
1156 showCancelButton: true,
1157 cancelButtonText: lpData.i18n.cancel,
1158 confirmButtonText: lpData.i18n.yes,
1159 reverseButtons: true,
1160 } ).then( ( result ) => {
1161 if ( result.isConfirmed ) {
1162 const editor = window.tinymce.get(
1163 `${ EditQuestion.selectors.elQuestionFibInput }-${ questionId }`
1164 );
1165 const elBlanks = editor.dom.select(
1166 `.${ EditQuestion.selectors.elQuestionFibInput }`
1167 );
1168 elBlanks.forEach( ( elBlank ) => {
1169 editor.dom.remove( elBlank, true );
1170 } );
1171
1172 dataAnswers.meta_data = {};
1173 this.setDataAnswersConfig( elQuestionEditMain, dataAnswers );
1174
1175 const elFibBlankOptions = elQuestionEditMain.querySelector(
1176 `${ EditQuestion.selectors.elFibBlankOptions }`
1177 );
1178 const elFibBlankOptionItems =
1179 elFibBlankOptions.querySelectorAll(
1180 `${ EditQuestion.selectors.elFibBlankOptionItem }:not(.clone)`
1181 );
1182 elFibBlankOptionItems.forEach( ( elFibBlankOptionItem ) => {
1183 elFibBlankOptionItem.remove();
1184 } );
1185
1186 const elBtnFibSaveContent = elQuestionEditMain.querySelector(
1187 `${ EditQuestion.selectors.elBtnFibSaveContent }`
1188 );
1189 lpUtils.lpSetLoadingEl( elBtnFibDeleteAllBlanks, 1 );
1190 this.fibSaveContent( {
1191 e: null,
1192 target: elBtnFibSaveContent,
1193 callBackCompleted: () => {
1194 lpUtils.lpSetLoadingEl( elBtnFibDeleteAllBlanks, 0 );
1195 },
1196 } );
1197 }
1198 } );
1199 }
1200 // Clear content FIB question
1201 fibClearContent( args ) {
1202 const { e, target } = args;
1203 const elBtnFibClearAllContent = target.closest(
1204 `${ EditQuestion.selectors.elBtnFibClearAllContent }`
1205 );
1206 if ( ! elBtnFibClearAllContent ) {
1207 return;
1208 }
1209
1210 const elQuestionEditMain = elBtnFibClearAllContent.closest(
1211 `${ EditQuestion.selectors.elQuestionEditMain }`
1212 );
1213 if ( ! elQuestionEditMain ) {
1214 return;
1215 }
1216
1217 const questionId = elQuestionEditMain.dataset.questionId;
1218 const dataAnswers = this.getDataAnswersConfig( elQuestionEditMain );
1219
1220 SweetAlert.fire( {
1221 title: elBtnFibClearAllContent.dataset.title,
1222 text: elBtnFibClearAllContent.dataset.content,
1223 icon: 'warning',
1224 showCloseButton: true,
1225 showCancelButton: true,
1226 cancelButtonText: lpData.i18n.cancel,
1227 confirmButtonText: lpData.i18n.yes,
1228 reverseButtons: true,
1229 } ).then( ( result ) => {
1230 if ( result.isConfirmed ) {
1231 const editor = window.tinymce.get(
1232 `lp-question-fib-input-${ questionId }`
1233 );
1234 editor.setContent( '' );
1235
1236 dataAnswers.meta_data = {};
1237 this.setDataAnswersConfig( elQuestionEditMain, dataAnswers );
1238
1239 const elFibBlankOptions = elQuestionEditMain.querySelector(
1240 `${ EditQuestion.selectors.elFibBlankOptions }`
1241 );
1242 const elFibBlankOptionItems =
1243 elFibBlankOptions.querySelectorAll(
1244 `${ EditQuestion.selectors.elFibBlankOptionItem }:not(.clone)`
1245 );
1246 elFibBlankOptionItems.forEach( ( elFibBlankOptionItem ) => {
1247 elFibBlankOptionItem.remove();
1248 } );
1249
1250 const elBtnFibSaveContent = elQuestionEditMain.querySelector(
1251 `${ EditQuestion.selectors.elBtnFibSaveContent }`
1252 );
1253 lpUtils.lpSetLoadingEl( elBtnFibClearAllContent, 1 );
1254 this.fibSaveContent( {
1255 e: null,
1256 target: elBtnFibSaveContent,
1257 callBackCompleted: () => {
1258 lpUtils.lpSetLoadingEl( elBtnFibClearAllContent, 0 );
1259 },
1260 } );
1261 }
1262 } );
1263 }
1264
1265 // Remove blank
1266 fibDeleteBlank( args ) {
1267 const { e, target } = args;
1268 const elBtnFibOptionDelete = target.closest(
1269 `${ EditQuestion.selectors.elBtnFibOptionDelete }`
1270 );
1271 if ( ! elBtnFibOptionDelete ) {
1272 return;
1273 }
1274
1275 const elQuestionEditMain = elBtnFibOptionDelete.closest(
1276 `${ EditQuestion.selectors.elQuestionEditMain }`
1277 );
1278 if ( ! elQuestionEditMain ) {
1279 return;
1280 }
1281
1282 const questionId = elQuestionEditMain.dataset.questionId;
1283 const elAnswersConfig = elQuestionEditMain.querySelector(
1284 `${ EditQuestion.selectors.elAnswersConfig }`
1285 );
1286 const dataAnswers = this.getDataAnswersConfig( elQuestionEditMain );
1287 const elFibBlankOptionItem = elBtnFibOptionDelete.closest(
1288 `${ EditQuestion.selectors.elFibBlankOptionItem }`
1289 );
1290 const blankId = elFibBlankOptionItem.dataset.id;
1291
1292 SweetAlert.fire( {
1293 title: elBtnFibOptionDelete.dataset.title,
1294 text: elBtnFibOptionDelete.dataset.content,
1295 icon: 'warning',
1296 showCloseButton: true,
1297 showCancelButton: true,
1298 cancelButtonText: lpData.i18n.cancel,
1299 confirmButtonText: lpData.i18n.yes,
1300 reverseButtons: true,
1301 } ).then( ( result ) => {
1302 if ( result.isConfirmed ) {
1303 // Find span with id on editor and remove it
1304 const editor = window.tinymce.get(
1305 `${ EditQuestion.selectors.elQuestionFibInput }-${ questionId }`
1306 );
1307 const elBlank = editor.dom.select(
1308 `.${ EditQuestion.selectors.elQuestionFibInput }[data-id="${ blankId }"]`
1309 );
1310 if ( elBlank[ 0 ] ) {
1311 // Remove tag html but keep content
1312 editor.dom.remove( elBlank[ 0 ], true );
1313 }
1314
1315 elFibBlankOptionItem.remove();
1316
1317 dataAnswers.meta_data = dataAnswers.meta_data || {};
1318 if ( dataAnswers.meta_data[ blankId ] ) {
1319 delete dataAnswers.meta_data[ blankId ];
1320 }
1321
1322 this.setDataAnswersConfig( elQuestionEditMain, dataAnswers );
1323
1324 const elBtnFibSaveContent = elQuestionEditMain.querySelector(
1325 `${ EditQuestion.selectors.elBtnFibSaveContent }`
1326 );
1327 lpUtils.lpSetLoadingEl( elFibBlankOptionItem, 1 );
1328 this.fibSaveContent( {
1329 e: null,
1330 target: elBtnFibSaveContent,
1331 callBackCompleted: () => {
1332 lpUtils.lpSetLoadingEl( elFibBlankOptionItem, 0 );
1333 },
1334 } );
1335 }
1336 } );
1337 }
1338
1339 // Change title of blank option
1340 fibOptionTitleInputChange( args ) {
1341 const { e, target } = args;
1342 const elFibOptionTitleInput = target.closest(
1343 `${ EditQuestion.selectors.elFibOptionTitleInput }`
1344 );
1345 if ( ! elFibOptionTitleInput ) {
1346 return;
1347 }
1348
1349 const elQuestionFibOptionItem = elFibOptionTitleInput.closest(
1350 `${ EditQuestion.selectors.elFibBlankOptionItem }`
1351 );
1352 if ( ! elQuestionFibOptionItem ) {
1353 return;
1354 }
1355
1356 const elQuestionEditMain = elFibOptionTitleInput.closest(
1357 `${ EditQuestion.selectors.elQuestionEditMain }`
1358 );
1359 if ( ! elQuestionEditMain ) {
1360 return;
1361 }
1362
1363 const value = elFibOptionTitleInput.value.trim();
1364 const blankId = elQuestionFibOptionItem.dataset.id;
1365 const questionId = elQuestionEditMain.dataset.questionId;
1366 const editor = window.tinymce.get(
1367 `lp-question-fib-input-${ questionId }`
1368 );
1369 const elBlank = editor.dom.select(
1370 `.lp-question-fib-input[data-id="${ blankId }"]`
1371 );
1372 if ( elBlank[ 0 ] ) {
1373 // Update content of blank
1374 elBlank[ 0 ].textContent = value;
1375 }
1376
1377 clearTimeout( timeoutAutoUpdateFib );
1378 timeoutAutoUpdateFib = setTimeout( () => {
1379 // Call ajax to update question description
1380 const elBtnFibSaveContent = elQuestionEditMain.querySelector(
1381 `${ EditQuestion.selectors.elBtnFibSaveContent }`
1382 );
1383 this.fibSaveContent( {
1384 e: null,
1385 target: elBtnFibSaveContent,
1386 } );
1387 }, 700 );
1388 }
1389
1390 // Save content FIB question
1391 fibSaveContent( args ) {
1392 const { e, target, callBackCompleted = null } = args;
1393 const elBtnFibSaveContent = target.closest(
1394 `${ EditQuestion.selectors.elBtnFibSaveContent }`
1395 );
1396 if ( ! elBtnFibSaveContent ) {
1397 return;
1398 }
1399
1400 const elQuestionEditMain = elBtnFibSaveContent.closest(
1401 `${ EditQuestion.selectors.elQuestionEditMain }`
1402 );
1403 const questionId = elQuestionEditMain.dataset.questionId;
1404
1405 const dataAnswers = this.getDataAnswersConfig( elQuestionEditMain );
1406 if ( ! dataAnswers ) {
1407 return;
1408 }
1409
1410 const editor = window.tinymce.get(
1411 `${ EditQuestion.selectors.elQuestionFibInput }-${ questionId }`
1412 );
1413 dataAnswers.title = editor.getContent();
1414
1415 const elFibBlankOptionItems = elQuestionEditMain.querySelectorAll(
1416 `${ EditQuestion.selectors.elFibBlankOptionItem }:not(.clone)`
1417 );
1418 if ( elFibBlankOptionItems ) {
1419 elFibBlankOptionItems.forEach( ( elFibBlankOptionItem ) => {
1420 const blankId = elFibBlankOptionItem.dataset.id;
1421 const elFibOptionMatchCaseInput =
1422 elFibBlankOptionItem.querySelector(
1423 `${ EditQuestion.selectors.elFibOptionMatchCaseInput }`
1424 );
1425 const elFibOptionComparisonInput =
1426 elFibBlankOptionItem.querySelector(
1427 `${ EditQuestion.selectors.elFibOptionComparisonInput }:checked`
1428 );
1429
1430 dataAnswers.meta_data[ blankId ].match_case =
1431 elFibOptionMatchCaseInput.checked ? 1 : 0;
1432 dataAnswers.meta_data[ blankId ].comparison =
1433 elFibOptionComparisonInput.value;
1434 } );
1435 }
1436
1437 //console.log( 'dataAnswers', dataAnswers );
1438
1439 if ( ! callBackCompleted ) {
1440 lpUtils.lpSetLoadingEl( elBtnFibSaveContent, 1 );
1441 }
1442
1443 // Call ajax to update answers config
1444 const callBack = {
1445 success: ( response ) => {
1446 const { message, status } = response;
1447
1448 if ( status === 'success' ) {
1449 this.setDataAnswersConfig(
1450 elQuestionEditMain,
1451 dataAnswers
1452 );
1453 } else {
1454 throw `Error: ${ message }`;
1455 }
1456
1457 lpToastify.show( message, status );
1458 },
1459 error: ( error ) => {
1460 lpToastify.show( error, 'error' );
1461 },
1462 completed: () => {
1463 if (
1464 callBackCompleted &&
1465 typeof callBackCompleted === 'function'
1466 ) {
1467 callBackCompleted();
1468 } else {
1469 lpUtils.lpSetLoadingEl( elBtnFibSaveContent, 0 );
1470 }
1471 },
1472 };
1473
1474 //console.log( 'dataAnswers', dataAnswers );
1475
1476 const dataSend = {
1477 action: 'update_question_answers_config',
1478 question_id: questionId,
1479 answers: dataAnswers,
1480 args: {
1481 id_url: idUrlHandle,
1482 },
1483 };
1484 window.lpAJAXG.fetchAJAX( dataSend, callBack );
1485 }
1486 // Show/hide match case option
1487 fibShowHideMatchCaseOption( args ) {
1488 const { e, target } = args;
1489 const elFibOptionMatchCaseInput = target.closest(
1490 `${ EditQuestion.selectors.elFibOptionMatchCaseInput }`
1491 );
1492 if ( ! elFibOptionMatchCaseInput ) {
1493 return;
1494 }
1495
1496 const elQuestionFibOptionDetail = elFibOptionMatchCaseInput.closest(
1497 `${ EditQuestion.selectors.elQuestionFibOptionDetail }`
1498 );
1499 const elFibOptionMatchCaseWrap =
1500 elQuestionFibOptionDetail.querySelector(
1501 `${ EditQuestion.selectors.elFibOptionMatchCaseWrap }`
1502 );
1503 if ( ! elQuestionFibOptionDetail || ! elFibOptionMatchCaseWrap ) {
1504 return;
1505 }
1506
1507 if ( elFibOptionMatchCaseInput.checked ) {
1508 lpUtils.lpShowHideEl( elFibOptionMatchCaseWrap, 1 );
1509 } else {
1510 lpUtils.lpShowHideEl( elFibOptionMatchCaseWrap, 0 );
1511 }
1512
1513 const elQuestionEditMain = elFibOptionMatchCaseInput.closest(
1514 `${ EditQuestion.selectors.elQuestionEditMain }`
1515 );
1516
1517 const elBtnFibSaveContent = elQuestionEditMain.querySelector(
1518 `${ EditQuestion.selectors.elBtnFibSaveContent }`
1519 );
1520
1521 elBtnFibSaveContent.click();
1522 }
1523 /***** End Fill in the blank question type *****/
1524
1525 // Generate a random string of specified length, for set unique id
1526 randomString( length = 10 ) {
1527 const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
1528 let result = '';
1529 for ( let i = 0; i < length; i++ ) {
1530 result += chars.charAt(
1531 Math.floor( Math.random() * chars.length )
1532 );
1533 }
1534 return result;
1535 }
1536 // Decode HTML entities
1537 decodeHtml( html ) {
1538 const txt = document.createElement( 'textarea' );
1539 txt.innerHTML = html;
1540 return txt.value;
1541 }
1542 }
1543
1544 const editQuestion = new EditQuestion();
1545 lpUtils.lpOnElementReady(
1546 EditQuestion.selectors.elEditQuestionWrap,
1547 ( elEditQuestionWrap ) => {
1548 const findClass = EditQuestion.selectors.elQuestionEditMain.replace(
1549 '.',
1550 ''
1551 );
1552 if ( ! elEditQuestionWrap.classList.contains( findClass ) ) {
1553 return;
1554 }
1555
1556 editQuestion.init();
1557 }
1558 );
1559