PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.3.8
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.3.8
4.4.8 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 All 139 releases
learnpress / assets / src / js / admin / edit-question.js

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

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