PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.8
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.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
← All changes | assets/src/js/admin/edit-question.js +1558 -1574 4.3.94.4.8 View file →
@@ -1,1574 +1,1558 @@
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 -);
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 +);