Two editors on one page works, but hotkeys no longer work
This commit is contained in:
parent
e41bfaddff
commit
5283d45c36
|
@ -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)
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %}
|
||||
|
|
Loading…
Reference in New Issue