Two editors on one page works, but hotkeys no longer work

This commit is contained in:
Eragon 2024-03-09 13:38:43 +01:00
parent e41bfaddff
commit 5283d45c36
Signed by: Eragon
GPG Key ID: 087126EBFC725006
5 changed files with 138 additions and 115 deletions

View File

@ -59,6 +59,8 @@ def edit_account():
form.signature.data = current_user.signature
form.biography.data = current_user.bio
form.signature.data = current_user.signature
form.biography.data = current_user.bio
return render('account/account.html', scripts=["+scripts/entropy.js"],
form=form)

View File

@ -4,7 +4,7 @@
flex-wrap: wrap;
align-items: center;
}
.editor .btn-group #filler {
.editor .btn-group .filler {
flex-grow: 1;
}
.editor .btn-group button {
@ -85,4 +85,4 @@
transform: translateX(-50%);
top: 50vh;
}
}
}

View File

@ -10,7 +10,7 @@ function editor_event_source(event)
/* Grab the button and the parent editor block. The onclick event itself
usually reports the SVG in the button as the source */
let node = event.target || event.srcElement;
let node = event.currentTarget || event.srcElement;
while (node != document.body) {
if (node.tagName == "BUTTON" && !button) {
button = node;
@ -35,9 +35,9 @@ function editor_event_source(event)
interval [start:end) (ie. the range where the contents are now located). */
function editor_replace_range(textarea, start, end, contents)
{
ta.value = ta.value.substring(0, start)
+ contents
+ ta.value.substring(end);
textarea.value = textarea.value.substring(0, start)
+ contents
+ textarea.value.substring(end);
return [start, start + contents.length];
}
@ -67,7 +67,7 @@ function editor_insert_around(event, before="", after=null)
ta.selectionStart = ta.selectionEnd = start + before.length;
}
preview();
preview(editor);
}
/* Event handler that modifies each line within the selection through a
@ -102,27 +102,28 @@ function editor_act_on_lines(event, fn)
ta.selectionStart = start;
ta.selectionEnd = end;
preview();
preview(editor);
}
function editor_clear_modals(event, close = true)
{
// Stop the propagation of the event
event.stopPropagation()
const [editor, button, ta] = editor_event_source(event);
// Reset all modal inputs
document.getElementById('media-alt-input').value = '';
document.getElementById('media-link-input').value = '';
document.getElementById('link-desc-input').value = '';
document.getElementById('link-link-input').value = '';
const media_type = document.getElementsByName("media-type");
editor.getElementsByClassName('media-alt-input')[0].value = '';
editor.getElementsByClassName('media-link-input')[0].value = '';
editor.getElementsByClassName('link-desc-input')[0].value = '';
editor.getElementsByClassName('link-link-input')[0].value = '';
const media_type = editor.getElementsByClassName("media-type")[0];
for(i = 0; i < media_type.length; i++) {
media_type[i].checked = false;
}
// Close all modal if requested
if (!close) { return }
const modals = document.getElementsByClassName('modal');
const modals = editor.getElementsByClassName('modal');
for (const i of modals) {i.style.display = 'none'};
}
@ -143,7 +144,8 @@ function editor_inline(event, type, enable_preview = true)
}
if (enable_preview) {
preview();
const [editor, button, ta] = editor_event_source(event);
preview(editor);
}
}
@ -172,11 +174,11 @@ function editor_insert_link(event, link_id, text_id, media = false)
let indexStart = ta.selectionStart;
let indexEnd = ta.selectionEnd;
const link = document.getElementById(link_id).value;
const text = document.getElementById(text_id).value;
const link = editor.getElementsByClassName(link_id)[0].value;
const text = editor.getElementsByClassName(text_id)[0].value;
let media_type = "";
const media_selector = document.getElementsByName("media-type");
const media_selector = editor.getElementsByClassName("media-type")[0];
for(i = 0; i < media_selector.length; i++) {
if (media_selector[i].checked) {
media_type = `{type=${media_selector[i].value}}`;
@ -197,7 +199,7 @@ function editor_insert_link(event, link_id, text_id, media = false)
ta.selectionStart = ta.selectionEnd = start + 1;
}
preview();
preview(editor);
}
function editor_title(event, level, diff)
@ -293,6 +295,8 @@ const DISABLE_PREVIEW_ICON = '<svg xmlns="http://www.w3.org/2000/svg" width="16"
const ENABLE_PREVIEW_ICON = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-eye" viewBox="0 0 16 16"><path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z"/><path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/></svg>';
function toggle_auto_preview() {
const [editor, button, ta] = editor_event_source(event);
let auto_preview;
if (document.cookie.split(";").some((item) => item.trim().startsWith("auto-preview="))) {
auto_preview = document.cookie.split(";").some((item) => item.includes("auto-preview=true"));
@ -301,25 +305,24 @@ function toggle_auto_preview() {
}
document.cookie = `auto-preview=${!auto_preview}; max-age=31536000; SameSite=Strict; Secure`
if (!auto_preview) {
document.getElementById("toggle_preview").title = "Désactiver la prévisualisation";
document.getElementById("toggle_preview").innerHTML = DISABLE_PREVIEW_ICON;
document.getElementById("manual_preview").style = "display: none";
editor.getElementsByClassName("toggle_preview")[0].title = "Désactiver la prévisualisation";
editor.getElementsByClassName("toggle_preview")[0].innerHTML = DISABLE_PREVIEW_ICON;
editor.getElementsByClassName("manual_preview")[0].style = "display: none";
} else {
document.getElementById("toggle_preview").title = "Activer la prévisualisation";
document.getElementById("toggle_preview").innerHTML = ENABLE_PREVIEW_ICON;
document.getElementById("manual_preview").style = "display: block";
editor.getElementsByClassName("toggle_preview")[0].title = "Activer la prévisualisation";
editor.getElementsByClassName("toggle_preview")[0].innerHTML = ENABLE_PREVIEW_ICON;
editor.getElementsByClassName("manual_preview")[0].style = "display: block";
}
}
/* This request the server to get a complete render of the current text in the textarea */
function preview(manual=false) {
function preview(editor, manual=false) {
// If auto-preview is disabled and the preview is not manually requested by the user
if (document.cookie.split(";").some((item) => item.includes("auto-preview=false")) && !manual) {
return;
}
const previewArea = document.querySelector("#editor_content_preview");
const textarea = document.querySelector(".editor textarea");
const previewArea = editor.querySelector(".editor_content_preview");
const ta = editor.querySelector("textarea");
const payload = {text: ta.value};
const headers = new Headers();
@ -341,86 +344,95 @@ function preview(manual=false) {
});
}
if (document.cookie.split(";").some((item) => item.trim().startsWith("auto-preview="))) {
if (document.cookie.split(";").some((item) => item.includes("auto-preview=false"))) {
document.getElementById("toggle_preview").title = "Activer la prévisualisation";
document.getElementById("toggle_preview").innerHTML = ENABLE_PREVIEW_ICON;
document.getElementById("manual_preview").style = "display: block";
}
/* Wrapper for user-requested preview refresh */
function manual_preview(editor_id) {
const editor = document.getElementById("editor_" + editor_id);
preview(editor, manual = true);
}
let previewTimeout = null;
let ta = document.querySelector(".editor textarea");
ta.addEventListener('keydown', function(e) {
// Tab insert some spaces
let keyCode = e.keyCode || e.which;
if (keyCode == 9) {
// TODO Add one tab to selected text without replacing it
e.preventDefault();
/* Add the event listener for the textarea hotkeys and auto-preview */
function editor_setup(editor_id) {
const editor = document.getElementById("editor_" + editor_id);
let start = e.target.selectionStart;
let end = e.target.selectionEnd;
// set textarea value to: text before caret + tab + text after caret
e.target.value = e.target.value.substring(0, start) + "\t" + e.target.value.substring(end);
e.target.selectionEnd = start + 1;
}
/*
* Keybindings for buttons. The default action of the keybinding is prevented.
* Ctrl+B adds bold
* Ctrl+I adds italic
* Ctrl+U adds underline
* Ctrl+S adds strikethrough
* Ctrl+H adds Header +1
* Ctrl+Enter send the form
*/
if (e.ctrlKey) {
switch (keyCode) {
case 13:
let t = e.target;
while(! (t instanceof HTMLFormElement)) {
t = t.parentNode;
}
try {
t.submit();
} catch(exception) {
t.submit.click();
}
e.preventDefault();
break;
case 66: // B
editor_inline(e, "bold", false);
e.preventDefault();
break;
case 72: // H
editor_title(e, 0, +1);
e.preventDefault();
break;
case 73: // I
editor_inline(e, "italic", false);
e.preventDefault();
break;
case 83: // S
editor_inline(e, "strike", false);
e.preventDefault();
break;
case 85: // U
editor_inline(e, "underline", false);
e.preventDefault();
break;
if (document.cookie.split(";").some((item) => item.trim().startsWith("auto-preview="))) {
if (document.cookie.split(";").some((item) => item.includes("auto-preview=false"))) {
editor.getElementsByClassName("toggle_preview")[0].title = "Activer la prévisualisation";
editor.getElementsByClassName("toggle_preview")[0].innerHTML = ENABLE_PREVIEW_ICON;
editor.getElementsByClassName("manual_preview")[0].style = "display: block";
}
}
// Set a timeout for refreshing the preview
if (previewTimeout != null) {
let previewTimeout = null;
let ta = editor.querySelector(".editor textarea");
ta.addEventListener('keydown', function(e) {
// Tab insert some spaces
let keyCode = e.keyCode || e.which;
if (keyCode == 9) {
// TODO Add one tab to selected text without replacing it
e.preventDefault();
let start = e.target.selectionStart;
let end = e.target.selectionEnd;
// set textarea value to: text before caret + tab + text after caret
e.target.value = e.target.value.substring(0, start) + "\t" + e.target.value.substring(end);
e.target.selectionEnd = start + 1;
}
/*
* Keybindings for buttons. The default action of the keybinding is prevented.
* Ctrl+B adds bold
* Ctrl+I adds italic
* Ctrl+U adds underline
* Ctrl+S adds strikethrough
* Ctrl+H adds Header +1
* Ctrl+Enter send the form
*/
if (e.ctrlKey) {
switch (keyCode) {
case 13:
let t = e.target;
while(! (t instanceof HTMLFormElement)) {
t = t.parentNode;
}
try {
t.submit();
} catch(exception) {
t.submit.click();
}
e.preventDefault();
break;
case 66: // B
editor_inline(e, "bold", false);
e.preventDefault();
break;
case 72: // H
editor_title(e, 0, +1);
e.preventDefault();
break;
case 73: // I
editor_inline(e, "italic", false);
e.preventDefault();
break;
case 83: // S
editor_inline(e, "strike", false);
e.preventDefault();
break;
case 85: // U
editor_inline(e, "underline", false);
e.preventDefault();
break;
}
}
// Set a timeout for refreshing the preview
clearTimeout(previewTimeout);
}
previewTimeout = setTimeout(preview, 3000);
});
previewTimeout = setTimeout(() => { preview(editor) }, 3000);
});
document.querySelector('emoji-picker').addEventListener('emoji-click', event => {
editor_clear_modals(event);
editor_insert_around(event, "", event.detail.unicode)
preview();
});
editor.querySelector('emoji-picker').addEventListener('emoji-click', event => {
editor_clear_modals(event);
editor_insert_around(event, "", event.detail.unicode)
preview(editor);
});
}

View File

@ -73,6 +73,9 @@
</div>
<div>
{{ widget_editor.text_editor(form.biography) }}
{% for error in form.signature.errors %}
<span class="msgerror">{{ error }}</span>
{% endfor %}
</div>
<h2>Préférences</h2>

View File

@ -1,5 +1,5 @@
{% macro text_editor(field, label=True, autofocus=false) %}
<div class="editor">
<div id="editor_{{ field.name}}" class="editor">
<!-- Field label if needed -->
{{ field.label if label }}
@ -113,9 +113,9 @@
<div class="modal" style="display: none">
<div>
<label for="link-desc-input">Description</label>
<input type="text" id="link-desc-input" name="link-desc-input">
<input type="text" class="link-desc-input" name="link-desc-input">
<label for="link-link-input">Lien</label>
<input type="url" id="link-link-input" name="link-link-input">
<input type="url" class="link-link-input" name="link-link-input">
</div>
<div>
<a type="button" class="button bg-ok" onclick="editor_insert_link(event, 'link-link-input', 'link-desc-input')">Valider</a>
@ -131,17 +131,17 @@
<div class="modal" style="display: none">
<div>
<label for="media-alt-input">Texte alternatif (sera affiché en cas d'erreur de chargement)</label>
<input type="text" id="media-alt-input" name="media-alt-input">
<input type="text" class="media-alt-input" name="media-alt-input">
<label for="media-link-input">Lien vers le média</label>
<input type="url" id="media-link-input" name="media-link-input">
<input type="url" class="media-link-input" name="media-link-input">
<fieldset title="Optionel mais peut être important si le type de média n'est pas correctement détecté">
<legend>
Type de média
</legend>
<label for="media-type-video" title="Vidéo">Vidéo</label>
<input type="radio" id="media-type-video" name="media-type" value="video" title="Vidéo" />
<input type="radio" class="media-type media-type-video" name="media-type" value="video" title="Vidéo" />
<label for="media-type-image" title="Image">Image</label>
<input type="radio" id="media-type-image" name="media-type" value="image" title="Image" />
<input type="radio" class="media-type media-type-image" name="media-type" value="image" title="Image" />
</fieldset>
</div>
<div>
@ -158,14 +158,14 @@
</svg>
</button>
-->
<div id="filler"></div>
<button id="manual_preview" type="button" onclick="preview(manual=true)" style="display: none" title="Rafraichir la prévisualisation">
<div class="filler"></div>
<button class="manual_preview" type="button" onclick="manual_preview('{{ field.name }}')" style="display: none" title="Rafraichir la prévisualisation">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-clockwise" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z"/>
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z"/>
</svg>
</button>
<button id="toggle_preview" type="button" onclick="toggle_auto_preview()" title="Désactiver la prévisualisation">
<button class="toggle_preview" type="button" onclick="toggle_auto_preview()" title="Désactiver la prévisualisation">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-eye-slash" viewBox="0 0 16 16">
<path d="M13.359 11.238C15.06 9.72 16 8 16 8s-3-5.5-8-5.5a7.028 7.028 0 0 0-2.79.588l.77.771A5.944 5.944 0 0 1 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.134 13.134 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755-.165.165-.337.328-.517.486l.708.709z"/>
<path d="M11.297 9.176a3.5 3.5 0 0 0-4.474-4.474l.823.823a2.5 2.5 0 0 1 2.829 2.829l.822.822zm-2.943 1.299.822.822a3.5 3.5 0 0 1-4.474-4.474l.823.823a2.5 2.5 0 0 0 2.829 2.829z"/>
@ -179,11 +179,17 @@
{{ field() }}
<!-- Comment preview -->
<div id="editor_content_preview"></div>
<div class="editor_content_preview"></div>
<!-- Display errors -->
{% for error in field.errors %}
<span class="msgerror">{{ error }}</span>
{% endfor %}
</div>
<script>
window.addEventListener("load", function(){
editor_setup("{{ field.name }}");
});
</script>
{% endmacro %}