Skip to content

Commit

Permalink
Implemented action tags and fixed bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
Oni01110011 committed Sep 29, 2024
1 parent c22201a commit 021a268
Showing 1 changed file with 136 additions and 90 deletions.
226 changes: 136 additions & 90 deletions amd/src/datalynx.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,21 @@
// This file is part of Moodle - http:// Moodle.org/.
//
// Moodle is free software: you can redistribute it and/or modify
// It under the terms of the GNU General Public License as published by
// The Free Software Foundation, either version 3 of the License, or
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// But WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// Along with Moodle. If not, see <http:// Www.gnu.org/licenses/>.
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

/**
* @package
* @copyright 2013 onwards David Bogner, Michael Pollak, Ivan Sakic and others.
* @copyright based on the work by 2011 Itamar Tzadok
* @license http:// Www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*
*/

/**
* Function to replace ##value## tags with buttons
* @param {*} editor
* Function to replace ##value## or [[field|behavior|renderer]] tags with buttons
* @param {*} editor - The TinyMCE editor instance
*/
function replaceTagsWithButtons(editor) {
const content = editor.getContent();
Expand All @@ -33,117 +25,163 @@ function replaceTagsWithButtons(editor) {
div.innerHTML = content;

div.innerHTML = div.innerHTML.replace(/##([^#]+)##/g, (match, action) => {
return `<button type="button" data-action-tag-button="true" data-datalynx-field="${action}">${action}</button>`;
return `<button type="button" contenteditable="false" data-action-tag-button="true"
data-datalynx-field="${action}">${action}</button>`;
});

div.innerHTML = div.innerHTML.replace(/\[\[([^\|\]]+)(?:\|([^\|\]]*))?(?:\|([^\|\]]*))?\]\]/g,
(match, field, behavior, renderer) => {
behavior = behavior || '';
renderer = renderer || '';
return `<button type="button" contenteditable="false" class="datalynx-field-tag" data-datalynx-field="
${field}" data-datalynx-behavior="${behavior}" data-datalynx-renderer="${renderer}">${field}</button>`;
});

editor.setContent(div.innerHTML);
}

/**
* Initialize the replacement of tags with buttons because get does not work without a method
* Reinitialize buttons to attach event listeners
* @param {*} editor - The TinyMCE editor instance
*/
function reInitializeButtons(editor) {
editor.getBody().querySelectorAll('button[data-action-tag-button="true"], button.datalynx-field-tag').forEach((button) => {
if (!button.getAttribute('data-click-initialized')) {
button.addEventListener('click', () => openMoodleDialog(button));
button.setAttribute('data-click-initialized', 'true');
}
});
}

/**
* Initialize buttons in TinyMCE after the editor is ready
*/
function initializeTinyMCEButtons() {
if (window.tinyMCE?.get) {
window.tinyMCE.get().forEach((editor) => {
editor.on('init', () => reInitializeButtons(editor));
editor.on('SetContent', () => reInitializeButtons(editor));
editor.on('change', () => reInitializeButtons(editor));
});
} else {
setTimeout(initializeTinyMCEButtons, 100);
}
}

/**
* Open Moodle dialog for tags (action or field)
* @param {*} button - The button element clicked
*/
function openMoodleDialog(button) {
const isFieldTag = button.classList.contains('datalynx-field-tag');
const dialogContent = document.createElement('div');

if (isFieldTag) {
dialogContent.innerHTML = `
<p>Field tag properties:</p>
<p>Field: ${button.textContent}</p>
<label>Behavior:</label>
<select id="tag-behavior-select">
<option value="behavior1">Behavior 1</option>
<option value="behavior2">Behavior 2</option>
</select>
<label>Renderer:</label>
<select id="tag-renderer-select">
<option value="renderer1">Renderer 1</option>
<option value="renderer2">Renderer 2</option>
</select>
<button type="button" id="delete-tag">Delete tag</button>
`;

document.getElementById('tag-behavior-select').value = button.getAttribute('data-datalynx-behavior');
document.getElementById('tag-renderer-select').value = button.getAttribute('data-datalynx-renderer');
} else {
dialogContent.innerHTML = `
<p>Action tag properties:</p>
<p>Action: ${button.textContent}</p>
<button type="button" id="delete-tag">Delete tag</button>
`;
}

const deleteButton = dialogContent.querySelector('#delete-tag');
deleteButton.addEventListener('click', () => {
button.remove();
dialog.hide();
});

const dialog = new M.core.dialogue({
bodyContent: dialogContent,
width: '400px',
draggable: true,
modal: true,
visible: true,
footerContent: ''
});
}

/**
* Initialize the replacement of tags with buttons because get() does not work without a method
*/
function initReplaceTagsWithButtons() {
window.tinyMCE.get().forEach((editor) => {
// Replace tags with buttons when editor is initialized
editor.on('init', () => replaceTagsWithButtons(editor));
// Reinitialize buttons after content is loaded or changed
editor.on('SetContent', () => reInitializeButtons(editor));
editor.on('change', () => reInitializeButtons(editor));
});
}

/**
* Wait for TinyMCE to be ready before initializing because get would not work in this context
* Wait for TinyMCE to be fully ready before initializing
*/
function waitForTinyMCE() {
if (window.tinyMCE?.activeEditor) {
// BUG: Button can not be pressed until line: 83 is executed !!!
if (window.tinyMCE?.activeEditor && window.tinyMCE.get().length > 0) {
initReplaceTagsWithButtons();
initializeTinyMCEButtons();
} else {
setTimeout(waitForTinyMCE, 100);
}
}


document.addEventListener('DOMContentLoaded', () => {
const dropdownMenu = document.getElementById('esection_editor_general_tag_menu');

if (typeof tinymce !== 'undefined') {
initReplaceTagsWithButtons();
waitForTinyMCE();
} else {
document.addEventListener('tinymce-editor-init', initReplaceTagsWithButtons);
}

dropdownMenu.addEventListener('change', () => {
const selectedValue = dropdownMenu.value;
const matches = selectedValue.match(/^##(.+?)##$/);
const contentToInsert = matches
? `<button type="button" class="action-tag-button" data-action-tag-button="true"
data-datalynx-field="${matches[1]}">${matches[1]}</button>`
: selectedValue;

window.tinyMCE.get().forEach((editor) => {
editor.insertContent(contentToInsert);
reInitializeButtons(editor);
});
});

/**
* Open Moodle dialog for tags
* @param {*} button b
*/
function openMoodleDialog(button) {
const dialogContent = document.createElement('div');
dialogContent.innerHTML = `
<p>Action tag properties:</p>
<p>${button.textContent}</p>
<button type="button">Delete tag</button>
`;

const deleteButton = dialogContent.querySelector('button');
deleteButton.addEventListener('click', () => {
button.remove();
dialog.hide();
});

const dialog = new M.core.dialogue({
bodyContent: dialogContent,
width: '400px',
draggable: true,
modal: true,
visible: true,
footerContent: ''
});
document.addEventListener('tinymce-editor-init', waitForTinyMCE);
}

/**
* Reinitialize buttons to attach event listeners
* @param {*} editor e
*/
function reInitializeButtons(editor) {
editor.getBody().querySelectorAll('button[data-action-tag-button="true"]').forEach((button) => {
if (!button.getAttribute('data-click-initialized')) {
button.addEventListener('click', () => openMoodleDialog(button));
button.setAttribute('data-click-initialized', 'true');
if (dropdownMenu) {
dropdownMenu.addEventListener('change', () => {
const selectedValue = dropdownMenu.value;

const actionTagMatch = selectedValue.match(/^##(.+?)##$/);
const fieldTagMatch = selectedValue.match(/^\[\[([^\|\]]+)(?:\|([^\|\]]*))?(?:\|([^\|\]]*))?\]\]$/);

let contentToInsert = '';

if (actionTagMatch) {
contentToInsert = `<button type="button" contenteditable="false" class="action-tag-button"
data-action-tag-button="true" data-datalynx-field="${actionTagMatch[1]}">${actionTagMatch[1]}</button>`;
} else if (fieldTagMatch) {
const field = fieldTagMatch[1];
const behavior = fieldTagMatch[2] || '';
const renderer = fieldTagMatch[3] || '';
contentToInsert = `<button type="button" contenteditable="false" class="datalynx-field-tag" data-datalynx-field="
${field}" data-datalynx-behavior="${behavior}" data-datalynx-renderer="${renderer}">${field}</button>`;
} else {
contentToInsert = selectedValue;
}
});
}

/**
* Initialize TinyMCE buttons after the editor is ready
*/
function initializeTinyMCEButtons() {
if (window.tinyMCE?.get) {
window.tinyMCE.get().forEach((editor) => {
editor.insertContent(contentToInsert);
reInitializeButtons(editor);

editor.on('init', () => reInitializeButtons(editor));
editor.on('SetContent change', () => reInitializeButtons(editor));
});
} else {
setTimeout(initializeTinyMCEButtons, 100);
}
});
}

initializeTinyMCEButtons();

// Handle form submission to replace buttons with tags
const form = document.querySelector('#datalynx-view-edit-form');
form.addEventListener('submit', (event) => {
window.tinyMCE.get().forEach((editor) => {
Expand All @@ -156,6 +194,14 @@ document.addEventListener('DOMContentLoaded', () => {
button.replaceWith(`##${action}##`);
});

div.querySelectorAll('button.datalynx-field-tag').forEach((button) => {
const field = button.getAttribute('data-datalynx-field');
const behavior = button.getAttribute('data-datalynx-behavior') || '';
const renderer = button.getAttribute('data-datalynx-renderer') || '';
const rawFieldTag = `[[${field}${behavior ? '|' + behavior : ''}${renderer ? '|' + renderer : ''}]]`;
button.replaceWith(rawFieldTag);
});

editor.setContent(div.innerHTML);
});
});
Expand Down

0 comments on commit 021a268

Please sign in to comment.