2
0
mirror of https://expo.survex.com/repositories/troggle/.git synced 2024-11-22 15:21:52 +00:00

Refactorising CodeMirror HTML editor, with an ultimate aim to make it reusable. However more work if required...

This commit is contained in:
Martin Green 2022-06-26 14:16:42 +01:00
parent 8f0ea8ed82
commit f1fcef2a6f
6 changed files with 210 additions and 233 deletions

View File

@ -130,3 +130,6 @@ class NewWebImageForm(forms.Form):
if full_path.exists(): if full_path.exists():
raise forms.ValidationError("File already exists in %s" % rel_path) raise forms.ValidationError("File already exists in %s" % rel_path)
return self.cleaned_data['file_'] return self.cleaned_data['file_']
class HTMLarea(forms.Textarea):
template_name = "widgets/HTMLarea.html"

View File

@ -23,6 +23,8 @@ import troggle.settings as settings
from troggle.lib import version_control from troggle.lib import version_control
from troggle.core.views.editor_helpers import HTMLarea
'''Formerly a separate package called 'flatpages' written by Martin Green 2011. '''Formerly a separate package called 'flatpages' written by Martin Green 2011.
This was NOT django.contrib.flatpages which stores HTML in the database, so the name was changed to expopages. This was NOT django.contrib.flatpages which stores HTML in the database, so the name was changed to expopages.
@ -380,5 +382,5 @@ class ExpoPageForm(forms.Form):
'''The form used by the editexpopage function '''The form used by the editexpopage function
''' '''
title = forms.CharField(widget=forms.TextInput(attrs={'size':'60', 'placeholder': "Enter title (displayed in tab)"})) title = forms.CharField(widget=forms.TextInput(attrs={'size':'60', 'placeholder': "Enter title (displayed in tab)"}))
html = forms.CharField(widget=forms.Textarea(attrs={"cols":80, "rows":20, 'placeholder': "Enter page content (using HTML)"})) html = forms.CharField(widget=HTMLarea(attrs={"cols":80, "rows":20, 'placeholder': "Enter page content (using HTML)"}))
change_message = forms.CharField(widget=forms.Textarea(attrs={"cols":80, "rows":3, 'placeholder': "Descibe the change made (for git)"})) change_message = forms.CharField(widget=forms.Textarea(attrs={"cols":80, "rows":3, 'placeholder': "Descibe the change made (for git)"}))

View File

@ -134,10 +134,13 @@ INSTALLED_APPS = (
'django.contrib.sessions', 'django.contrib.sessions',
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.admindocs', 'django.contrib.admindocs',
'django.forms', #Required to customise widget templates
# 'django.contrib.staticfiles', # We put our CSS etc explicitly in the right place so do not need this # 'django.contrib.staticfiles', # We put our CSS etc explicitly in the right place so do not need this
'troggle.core', 'troggle.core',
) )
FORM_RENDERER = 'django.forms.renderers.TemplatesSetting' #Required to customise widget templates
# See the recommended order of these in https://docs.djangoproject.com/en/2.2/ref/middleware/ # See the recommended order of these in https://docs.djangoproject.com/en/2.2/ref/middleware/
# Note that this is a radically different onion architecture from earlier versions though it looks the same, # Note that this is a radically different onion architecture from earlier versions though it looks the same,
# see https://docs.djangoproject.com/en/2.0/topics/http/middleware/#upgrading-pre-django-1-10-style-middleware # see https://docs.djangoproject.com/en/2.0/topics/http/middleware/#upgrading-pre-django-1-10-style-middleware

View File

@ -2,243 +2,14 @@
{% block title %}Edit {{ path }}{% endblock %} {% block title %}Edit {{ path }}{% endblock %}
{% block extrahead %} {% block extrahead %}
<!--<script src="{{ settings.TINY_MCE_MEDIA_URL }}tiny_mce.js" type="text/javascript"></script>-->
<!-- <script type="text/javascript"> tinyMCE.init({ mode : "textareas" }); </script>-->
<script src="{{ settings.MEDIA_URL }}admin/js/vendor/jquery/jquery.js" type="text/javascript"></script>
<script src={{ settings.MEDIA_URL }}codemirror/codemirror.js></script>
<script src={{ settings.MEDIA_URL }}codemirror/xml.js></script>
<script src={{ settings.MEDIA_URL }}codemirror/javascript.js></script>
<script src={{ settings.MEDIA_URL }}codemirror/css.js></script>
<script src={{ settings.MEDIA_URL }}codemirror/htmlmixed.js></script>
<link rel=stylesheet href={{ settings.MEDIA_URL }}codemirror/codemirror.css>
<link rel=stylesheet href={{ settings.MEDIA_URL }}codemirror/docs.css>
<style type=text/css>
.CodeMirror {
float: left;
width: 50%;
border: 1px solid black;
height: 80%;
}
.CodeMirror-scroll{
height: 99%
}
iframe {
width: 49%;
height: 80%;
float: left;
border: 1px solid black;
border-left: 0px;
}
body{max-width: none;
margin-left: 275px;
margin-right: 50px;
text-align: left;
}
#id_change_message{
height: 5%;
}
</style>
<style type=text/css>
html {
font-family: "Helvetica Neue", sans-serif;
width: 100%;
color: #666666;
text-align: center;
}
.popup-overlay {
/*Hides pop-up when there is no "active" class*/
visibility: hidden;
position: absolute;
background: #ffffff;
border: 3px solid #666666;
width: 90%;
height: 80%;
overflow: scroll;
left: 5%;
z-index: 20;
}
.popup-overlay.active {
/*displays pop-up when "active" class is present*/
visibility: visible;
text-align: center;
}
.popup-content {
/*Hides pop-up content when there is no "active" class */
visibility: hidden;
}
.popup-content.active {
/*Shows pop-up content when "active" class is present */
visibility: visible;
}
button {
display: inline-block;
vertical-align: middle;
border-radius: 30px;
margin: .20rem;
font-size: 1rem;
color: #666666;
background: #ffffff;
border: 1px solid #666666;
}
button:hover {
border: 1px solid #666666;
background: #666666;
color: #ffffff;
}
</style>
{% endblock %} {% endblock %}
{% block body %} {% block body %}
<h1>Edit {{ path }}</h1> <h1>Edit {{ path }}</h1>
<!--Creates the add image popup-->
<div class="add-image-popup popup-overlay">
<div class="add-image-popup popup-content">
<h2>Select Image</h2>
<p id="image_popup_content"> Loading ...</p>
<button onclick="new_image_popup()">Upload Image</button>
<button class="close" onclick="$('.add-image-popup').removeClass('active');">Close</button>
</div>
</div>
<!--Creates the new image popup-->
<div class="new-image-popup popup-overlay">
<div class="new-image-popup popup-content">
<h2>New Image</h2>
<p id="new_image_popup_content"> Loading ...</p>
<button class="close" onclick="$('.new-image-popup').removeClass('active');">Close</button>
</div>
</div>
<form action="" method="post">{% csrf_token %} <form action="" method="post">{% csrf_token %}
{{ form.non_field_errors }} {{ form.as_p }}
<div class="fieldWrapper">
{{ form.title.errors }}
<label for="{{ form.title.id_for_label }}">Title:</label>
{{ form.title }}
</div>
<div class="fieldWrapper">
{{ form.html.errors }}
<!--<label for="{{ form.title.id_for_label }}">HTML:</label> -->
{{ form.html }}<iframe id=preview></iframe>
</div>
<button type="button" onclick="addTag('i', '')">italic</button>
<button type="button" onclick="addTag('b', '')">bold</button>
<button type="button" onclick="addTag('h2', 'id=&quot;tophead&quot;')">top heading</button>
<button type="button" onclick="addTag('h1', '')">heading 1</button>
<button type="button" onclick="addTag('h2', '')">heading 2</button>
<button type="button" onclick="addTag('h3', '')">heading 3</button>
<button type="button" onclick="addTag('h4', '')">heading 4</button>
<button type="button" onclick="addTag('a', 'href=&quot;&quot;')">hyperlink</button>
<button type="button" onclick="addTag('p', '')">paragraph</button>
<button type="button" onclick="add_image_popup()">image</button>
<div class="fieldWrapper">
{{ form.change_message.errors }}
<label for="{{ form.title.id_for_label }}">Git change message:</label>
{{ form.change_message }}
</div>
{% include "menu.html" %}
<p><input type="submit" value="Submit" /></p> <p><input type="submit" value="Submit" /></p>
</form> </form>
{% include "menu.html" %}
{% include 'html_editor_scripts_css.html' %}
<script>
function add_image_popup() {
$('.add-image-popup').addClass('active');
$('#image_popup_content').load("{% url 'image_selector' path %}", function() {
$('.thumbnail').click(function(){
$(".add-image-popup").removeClass("active");
addStr($( this ).attr("data-html"))
});
})
}
function new_image_popup() {
$('.add-image-popup').removeClass('active');
$('.new-image-popup').addClass('active');
$.ajax({
type : "GET",
dataType: "json",
url: "{% url 'new_image_form' path %}",
success: function(data){handle_new_image(data)}
});
}
function handle_new_image(data) {
if (data.hasOwnProperty('form')) {
$('#new_image_popup_content').html(data.form);
$('#new_image_form').on('submit', function(e){
e.preventDefault();
data = $('#new_image_form').serialize();
$.ajax({
type : "POST",
dataType: "json",
url: "{% url 'new_image_form' path %}",
data: new FormData($('#new_image_form')[0]),
processData: false,
contentType: false,
success: function(data){
handle_new_image(data);
}
});
});
}
else if (data.hasOwnProperty('html')) {
$('.new-image-popup').removeClass('active');
addStr(data.html);
}
else {
alert(data.error);
}
}
</script>
<script>
var delay;
// Initialize CodeMirror editor with a nice html5 canvas demo.
var editor = CodeMirror.fromTextArea(document.getElementById('id_html'), {
mode: 'text/html',
tabMode: 'indent',
onChange: function() {
clearTimeout(delay);
delay = setTimeout(updatePreview, 300);
}
});
function updatePreview() {
var previewFrame = document.getElementById('preview');
var preview = previewFrame.contentDocument || previewFrame.contentWindow.document;
preview.open();
preview.write("<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8' /><link rel='stylesheet' type='text/css' href='/css/main2.css' /> <style type=text/css>body{max-width: none;margin-left: 15px;margin-right: 15px;}</style></head><body>");
preview.write(editor.getValue());
preview.write("</body></html>");
preview.close();
}
setTimeout(updatePreview, 300);
function addTag(tag, attr){
// For codemirror & center cursor
var from = editor.getCursor(true);
var to = editor.getCursor(false);
editor.replaceRange("</"+tag+">", to);
if (attr.length > 0) {attr = " " + attr;}
editor.replaceRange("<"+tag+attr+">", from);
editor.focus();
editor.setCursor({line: to.line , ch : to.ch + 2 + tag.length + attr.length });
}
function addStr(x){
var to = editor.getCursor(false);
editor.replaceRange(x, to);
editor.focus();
editor.setCursor({line: to.line , ch : to.ch + x.length });
}
</script>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,166 @@
<script src="{{ settings.MEDIA_URL }}admin/js/vendor/jquery/jquery.js" type="text/javascript"></script>
<script src={{ settings.MEDIA_URL }}codemirror/codemirror.js></script>
<script src={{ settings.MEDIA_URL }}codemirror/xml.js></script>
<script src={{ settings.MEDIA_URL }}codemirror/javascript.js></script>
<script src={{ settings.MEDIA_URL }}codemirror/css.js></script>
<script src={{ settings.MEDIA_URL }}codemirror/htmlmixed.js></script>
<link rel=stylesheet href={{ settings.MEDIA_URL }}codemirror/codemirror.css>
<link rel=stylesheet href={{ settings.MEDIA_URL }}codemirror/docs.css>
<style type=text/css>
.CodeMirror {
float: left;
width: 50%;
border: 1px solid black;
height: 80%;
}
.CodeMirror-scroll{
height: 99%
}
iframe {
width: 49%;
height: 80%;
float: left;
border: 1px solid black;
border-left: 0px;
}
body{max-width: none;
margin-left: 275px;
margin-right: 50px;
text-align: left;
}
#id_change_message{
height: 5%;
}
</style>
<style type=text/css>
html {
font-family: "Helvetica Neue", sans-serif;
width: 100%;
color: #666666;
text-align: center;
}
.popup-overlay {
/*Hides pop-up when there is no "active" class*/
visibility: hidden;
position: absolute;
background: #ffffff;
border: 3px solid #666666;
width: 90%;
height: 80%;
overflow: scroll;
left: 5%;
z-index: 20;
}
.popup-overlay.active {
/*displays pop-up when "active" class is present*/
visibility: visible;
text-align: center;
}
.popup-content {
/*Hides pop-up content when there is no "active" class */
visibility: hidden;
}
.popup-content.active {
/*Shows pop-up content when "active" class is present */
visibility: visible;
}
</style>
<script>
function add_image_popup() {
$('.add-image-popup').addClass('active');
$('#image_popup_content').load("{% url 'image_selector' path %}", function() {
$('.thumbnail').click(function(){
$(".add-image-popup").removeClass("active");
addStr($( this ).attr("data-html"))
});
})
}
function new_image_popup() {
$('.add-image-popup').removeClass('active');
$('.new-image-popup').addClass('active');
$.ajax({
type : "GET",
dataType: "json",
url: "{% url 'new_image_form' path %}",
success: function(data){handle_new_image(data)}
});
}
function handle_new_image(data) {
if (data.hasOwnProperty('form')) {
$('#new_image_popup_content').html(data.form);
$('#new_image_form').on('submit', function(e){
e.preventDefault();
data = $('#new_image_form').serialize();
$.ajax({
type : "POST",
dataType: "json",
url: "{% url 'new_image_form' path %}",
data: new FormData($('#new_image_form')[0]),
processData: false,
contentType: false,
success: function(data){
handle_new_image(data);
}
});
});
}
else if (data.hasOwnProperty('html')) {
$('.new-image-popup').removeClass('active');
addStr(data.html);
}
else {
alert(data.error);
}
}
</script>
<script>
var delay;
// Initialize CodeMirror editor with a nice html5 canvas demo.
var editor = CodeMirror.fromTextArea(document.getElementById('id_html'), {
mode: 'text/html',
tabMode: 'indent',
onChange: function() {
clearTimeout(delay);
delay = setTimeout(updatePreview, 300);
}
});
function updatePreview() {
var previewFrame = document.getElementById('preview');
var preview = previewFrame.contentDocument || previewFrame.contentWindow.document;
preview.open();
preview.write("<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8' /><link rel='stylesheet' type='text/css' href='/css/main2.css' /> <style type=text/css>body{max-width: none;margin-left: 15px;margin-right: 15px;}</style></head><body>");
preview.write(editor.getValue());
preview.write("</body></html>");
preview.close();
}
setTimeout(updatePreview, 300);
function addTag(tag, attr){
// For codemirror & center cursor
var from = editor.getCursor(true);
var to = editor.getCursor(false);
editor.replaceRange("</"+tag+">", to);
if (attr.length > 0) {attr = " " + attr;}
editor.replaceRange("<"+tag+attr+">", from);
editor.focus();
editor.setCursor({line: to.line , ch : to.ch + 2 + tag.length + attr.length });
}
function addStr(x){
var to = editor.getCursor(false);
editor.replaceRange(x, to);
editor.focus();
editor.setCursor({line: to.line , ch : to.ch + x.length });
}
</script>

View File

@ -0,0 +1,32 @@
<!--Creates the add image popup-->
<div><div class="add-image-popup popup-overlay">
<div class="add-image-popup popup-content">
<h2>Select Image</h2>
<p id="image_popup_content"> Loading ...</p>
<button onclick="new_image_popup()">Upload Image</button>
<button class="close" onclick="$('.add-image-popup').removeClass('active');">Close</button>
</div>
</div>
<!--Creates the new image popup-->
<div class="new-image-popup popup-overlay">
<div class="new-image-popup popup-content">
<h2>New Image</h2>
<p id="new_image_popup_content"> Loading ...</p>
<button class="close" onclick="$('.new-image-popup').removeClass('active');">Close</button>
</div>
</div>
{% include "django/forms/widgets/textarea.html" %}
<iframe id=preview></iframe>
<button type="button" onclick="addTag('i', '')">italic</button>
<button type="button" onclick="addTag('b', '')">bold</button>
<button type="button" onclick="addTag('h2', 'id=&quot;tophead&quot;')">top heading</button>
<button type="button" onclick="addTag('h1', '')">heading 1</button>
<button type="button" onclick="addTag('h2', '')">heading 2</button>
<button type="button" onclick="addTag('h3', '')">heading 3</button>
<button type="button" onclick="addTag('h4', '')">heading 4</button>
<button type="button" onclick="addTag('a', 'href=&quot;&quot;')">hyperlink</button>
<button type="button" onclick="addTag('p', '')">paragraph</button>
<button type="button" onclick="add_image_popup()">image</button>
<button type="button" onclick="addStr('<hr/>')">horizontal line</button>
</div>