first commit

This commit is contained in:
jideguru
2021-05-26 23:42:34 +01:00
commit 386a8f518a
89 changed files with 3630 additions and 0 deletions

18
assets/editor/editor.html Normal file
View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css" href="normalize.css">
<link rel="stylesheet" type="text/css" href="style.css">
<link rel="stylesheet" type="text/css" href="platform_style.css">
</head>
<body>
<div id="editor" contenteditable="true"></div>
<script type="text/javascript" src="interact.min.js"></script>
<script type="text/javascript" src="rich_text_editor.js"></script>
</body>
</html>

23
assets/editor/index.html Normal file
View File

@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Spoon-Knife</title>
<LINK href="styles.css" rel="stylesheet" type="text/css">
</head>
<body>
<img src="forkit.gif" id="octocat" alt="" />
<!-- Feel free to change this text here -->
<p>
Fork me? Fork you, @octocat!
</p>
<p>
Sean made a change
</p>
</body>
</html>

6
assets/editor/interact.min.js vendored Normal file

File diff suppressed because one or more lines are too long

412
assets/editor/normalize.css vendored Normal file
View File

@@ -0,0 +1,412 @@
/*! normalize.css v3.0.2 | MIT License | git.io/normalize */
/**
* 1. Set default font family to sans-serif.
* 2. Prevent iOS text size adjust after orientation change, without disabling
* user zoom.
*/
html {
font-family: roboto; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/**
* Remove default margin.
*/
body {
margin: 0;
}
/* HTML5 display definitions
========================================================================== */
/**
* Correct `block` display not defined for any HTML5 element in IE 8/9.
* Correct `block` display not defined for `details` or `summary` in IE 10/11
* and Firefox.
* Correct `block` display not defined for `main` in IE 11.
*/
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
menu,
nav,
section,
summary {
display: block;
}
/**
* 1. Correct `inline-block` display not defined in IE 8/9.
* 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
*/
audio,
canvas,
progress,
video {
display: inline-block; /* 1 */
vertical-align: baseline; /* 2 */
}
/**
* Prevent modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/**
* Address `[hidden]` styling not present in IE 8/9/10.
* Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.
*/
[hidden],
template {
display: none;
}
/* Links
========================================================================== */
/**
* Remove the gray background color from active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* Improve readability when focused and also mouse hovered in all browsers.
*/
a:active,
a:hover {
outline: 0;
}
/* Text-level semantics
========================================================================== */
/**
* Address styling not present in IE 8/9/10/11, Safari, and Chrome.
*/
abbr[title] {
border-bottom: 1px dotted;
}
/**
* Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
*/
b,
strong {
font-weight: bold;
}
/**
* Address styling not present in Safari and Chrome.
*/
dfn {
font-style: italic;
}
/**
* Address variable `h1` font-size and margin within `section` and `article`
* contexts in Firefox 4+, Safari, and Chrome.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/**
* Address styling not present in IE 8/9.
*/
mark {
background: #ff0;
color: #000;
}
/**
* Address inconsistent and variable font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` affecting `line-height` in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
/* Embedded content
========================================================================== */
/**
* Remove border when inside `a` element in IE 8/9/10.
*/
img {
border: 0;
}
/**
* Correct overflow not hidden in IE 9/10/11.
*/
svg:not(:root) {
overflow: hidden;
}
/* Grouping content
========================================================================== */
/**
* Address margin not present in IE 8/9 and Safari.
*/
figure {
margin: 1em 40px;
}
/**
* Address differences between Firefox and other browsers.
*/
hr {
box-sizing: content-box;
height: 0;
}
/**
* Contain overflow in all browsers.
*/
pre {
overflow: auto;
}
/**
* Address odd `em`-unit font size rendering in all browsers.
*/
code,
kbd,
pre,
samp {
font-family: monospace, monospace;
font-size: 1em;
}
/* Forms
========================================================================== */
/**
* Known limitation: by default, Chrome and Safari on OS X allow very limited
* styling of `select`, unless a `border` property is set.
*/
/**
* 1. Correct color not being inherited.
* Known issue: affects color of disabled elements.
* 2. Correct font properties not being inherited.
* 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
*/
button,
input,
optgroup,
select,
textarea {
color: inherit; /* 1 */
font: inherit; /* 2 */
margin: 0; /* 3 */
}
/**
* Address `overflow` set to `hidden` in IE 8/9/10/11.
*/
button {
overflow: visible;
}
/**
* Address inconsistent `text-transform` inheritance for `button` and `select`.
* All other form control elements do not inherit `text-transform` values.
* Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
* Correct `select` style inheritance in Firefox.
*/
button,
select {
text-transform: none;
}
/**
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
* and `video` controls.
* 2. Correct inability to style clickable `input` types in iOS.
* 3. Improve usability and consistency of cursor style between image-type
* `input` and others.
*/
button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button; /* 2 */
cursor: pointer; /* 3 */
}
/**
* Re-set default cursor for disabled elements.
*/
button[disabled],
html input[disabled] {
cursor: default;
}
/**
* Address Firefox 4+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
input {
line-height: normal;
}
/**
* It's recommended that you don't attempt to style these elements.
* Firefox's implementation doesn't respect box-sizing, padding, or width.
*
* 1. Address box sizing set to `content-box` in IE 8/9/10.
* 2. Remove excess padding in IE 8/9/10.
*/
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Fix the cursor style for Chrome's increment/decrement buttons. For certain
* `font-size` values of the `input`, it causes the cursor style of the
* decrement button to change from `default` to `text`.
*/
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Address `appearance` set to `searchfield` in Safari and Chrome.
* 2. Address `box-sizing` set to `border-box` in Safari and Chrome
*/
input[type="search"] {
-webkit-appearance: textfield; /* 1 */
-webkit-box-sizing: content-box; /* 2 */
box-sizing: content-box;
}
/**
* Remove inner padding and search cancel button in Safari and Chrome on OS X.
* Safari (but not Chrome) clips the cancel button when the search input has
* padding (and `textfield` appearance).
*/
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* Define consistent border, margin, and padding.
*/
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
/**
* 1. Correct `color` not being inherited in IE 8/9/10/11.
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
*/
legend {
border: 0; /* 1 */
padding: 0; /* 2 */
}
/**
* Remove default vertical scrollbar in IE 8/9/10/11.
*/
textarea {
overflow: auto;
}
/**
* Don't inherit the `font-weight` (applied by a rule above).
* NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
*/
optgroup {
font-weight: bold;
}
/* Tables
========================================================================== */
/**
* Remove most spacing between table cells.
*/
table {
border-collapse: collapse;
border-spacing: 0;
}
td,
th {
padding: 0;
}

View File

@@ -0,0 +1,8 @@
body {
font-family: serif;
font-size: 18px;
}
h1, h2, h3, h4, h5, h6 {
font-family: sans-serif;
}

View File

@@ -0,0 +1,699 @@
// also edit class name in style.css when changing this.
const resizableImageClass = "resizable";
const EditorDefaultHtml = "<p></p>";
var editor = {
_textField: document.getElementById('editor'),
_htmlSetByApplication: null,
_currentSelection: {
"startContainer": 0,
"startOffset": 0,
"endContainer": 0,
"endOffset": 0
},
_useWindowLocationForEditorStateChangedCallback: false,
_imageMinWidth: 100,
_imageMinHeight: 50,
_isImageResizingEnabled: true,
init: function() {
document.addEventListener("selectionchange", function() {
editor._backupRange();
editor._handleTextEntered(); // in newly selected area different commands may be activated / deactivated
});
this._textField.addEventListener("keydown", function(e) {
var BACKSPACE = 8;
var M = 77;
if(e.which == BACKSPACE) {
if(editor._textField.innerText.length == 1) { // prevent that first paragraph gets deleted
e.preventDefault();
return false;
}
}
else if(e.which == M && e.ctrlKey) { // TODO: what is Ctrl + M actually good for?
e.preventDefault(); // but be aware in this way also (JavaFX) application won't be able to use Ctrl + M
return false;
}
});
this._textField.addEventListener("keyup", function(e) {
if(e.altKey || e.ctrlKey) { // some key combinations activate commands like CTRL + B setBold() -> update editor state so that UI is aware of this
editor._updateEditorState();
}
});
this._textField.addEventListener("paste", function(e) { editor._handlePaste(e); });
this._ensureEditorInsertsParagraphWhenPressingEnter();
this._initDragImageToResize();
this._updateEditorState();
},
_ensureEditorInsertsParagraphWhenPressingEnter: function() {
// see https://stackoverflow.com/a/36373967
this._executeCommand("DefaultParagraphSeparator", "p");
this._textField.innerHTML = ""; // clear previous content
var newElement = document.createElement("p");
newElement.innerHTML = "&#8203";
this._textField.appendChild(newElement);
var selection=document.getSelection();
var range=document.createRange();
range.setStart(newElement.firstChild, 1);
selection.removeAllRanges();
selection.addRange(range);
},
_initDragImageToResize: function() {
var angle = 0;
interact.addDocument(window.document, {
events: { passive: false },
});
interact('img.' + resizableImageClass)
.draggable({
onmove: window.dragMoveListener,
restrict: {
restriction: 'parent',
elementRect: { top: 0, left: 0, bottom: 1, right: 1 }
},
})
.resizable({
// resize from right or bottom
edges: { top: true, left: true, right: true, bottom: true},
// keep the edges inside the parent
restrictEdges: {
outer: 'parent',
endOnly: true,
},
// minimum size
restrictSize: {
min: { width: this._imageMinWidth, height: this._imageMinHeight },
},
inertia: true,
preserveAspectRatio: true,
})
.gesturable({
onmove: function (event) {
var target = event.target;
angle += event.da;
if(Math.abs(90 - (angle % 360)) < 10){ angle = 90;}
if(Math.abs(180 - (angle % 360)) < 10){ angle = 180;}
if(Math.abs(270 - (angle % 360)) < 10){ angle = 270;}
if(Math.abs(angle % 360) < 10){ angle = 0;}
target.style.webkitTransform =
target.style.transform =
'rotate(' + angle + 'deg)';
}
})
.on('resizemove', function (event) {
var target = event.target,
x = (parseFloat(target.getAttribute('data-x')) || 0),
y = (parseFloat(target.getAttribute('data-y')) || 0);
// update the element's style
target.style.width = event.rect.width + 'px';
target.style.height = event.rect.height + 'px';
target.width = event.rect.width + 'px';
target.height = event.rect.height + 'px';
target.setAttribute('data-x', x);
target.setAttribute('data-y', y);
});
},
_handleTextEntered: function() {
if(this._getHtml() == "<p><br></p>") { // SwiftKey, when deleting all entered text, inserts a pure "<br>" therefore check for <p>&#8203</p> doesn't work anymore
this._ensureEditorInsertsParagraphWhenPressingEnter();
}
this._updateEditorState();
},
_handlePaste: function(event) {
var clipboardData = event.clipboardData || window.clipboardData;
var pastedData = clipboardData.getData('text/html') || clipboardData.getData('text').replace(/(?:\r\n|\r|\n)/g, '<br />'); // replace new lines // TODO: may use 'text/plain' instead of 'text'
this._waitTillPastedDataInserted(event, pastedData);
},
_waitTillPastedDataInserted: function(event, pastedData) {
var previousHtml = this._getHtml();
setTimeout(function () { // on paste event inserted text is not inserted yet -> wait for till text has been inserted
editor._waitTillPastedTextInserted(previousHtml, 10, pastedData); // max 10 tries, after that we give up to prevent endless loops
}, 100);
},
_waitTillPastedTextInserted: function(previousHtml, iteration, pastedData) {
var hasBeenInserted = this._getHtml() != previousHtml;
if(hasBeenInserted || ! iteration) {
// there seems to be a bug (on Linux only?) when pasting data e.g. from Firefox: then only '' gets inserted
if((this._getHtml().indexOf('​ÿþ&lt;') !== -1 || this._getHtml().indexOf('ÿþ&lt;<br>') !== -1) && previousHtml.indexOf('​​ÿþ&lt;') === -1) {
this._textField.innerHTML = this._getHtml().replace('​ÿþ&lt;', pastedData).replace('ÿþ&lt;<br>', pastedData);
// TODO: set caret to end of pasted data
}
this._updateEditorState();
}
else {
setTimeout(function () { // wait for till pasted data has been inserted
editor._waitTillPastedTextInserted(pastedText, iteration - 1);
}, 100);
}
},
_getHtml: function() {
return this._textField.innerHTML;
},
_getHtmlWithoutInternalModifications: function() {
var clonedHtml = this._textField.cloneNode(true);
this._removeResizeImageClasses(clonedHtml);
return clonedHtml.innerHTML;
},
getEncodedHtml: function() {
return encodeURIComponent(this._getHtmlWithoutInternalModifications());
},
setHtml: function(html, baseUrl) {
if(baseUrl) {
this._setBaseUrl(baseUrl);
}
if(html.length != 0) {
var decodedHtml = this._decodeHtml(html);
this._textField.innerHTML = decodedHtml;
this._htmlSetByApplication = decodedHtml;
if(this._isImageResizingEnabled) {
this.makeImagesResizeable();
}
}
else {
this._ensureEditorInsertsParagraphWhenPressingEnter();
this._htmlSetByApplication = null;
}
this.didHtmlChange = false;
},
_decodeHtml: function(html) {
return decodeURIComponent(html.replace(/\+/g, '%20'));
},
_setBaseUrl: function(baseUrl) {
var baseElements = document.head.getElementsByTagName('base');
var baseElement = null;
if(baseElements.length > 0) {
baseElement = baseElements[0];
}
else {
var baseElement = document.createElement('base');
document.head.appendChild(baseElement); // don't know why but append() is not available
}
baseElement.setAttribute('href', baseUrl);
baseElement.setAttribute('target', '_blank');
},
useWindowLocationForEditorStateChangedCallback: function() {
this._useWindowLocationForEditorStateChangedCallback = true;
},
makeImagesResizeable: function() {
this._isImageResizingEnabled = true;
var images = document.getElementsByTagName("img");
for(var i = 0; i < images.length; i++) {
this._addClass(images[i], resizableImageClass);
}
},
disableImageResizing: function() {
this._isImageResizingEnabled = false;
this._removeResizeImageClasses(document);
},
_removeResizeImageClasses: function(document) {
var images = document.getElementsByTagName("img");
for(var i = 0; i < images.length; i++) {
this._removeClass(images[i], resizableImageClass);
}
},
_hasClass: function(element, className) {
return !!element.className.match(new RegExp('(\\s|^)' + className +'(\\s|$)'));
},
_addClass: function(element, className) {
if (this._hasClass(element, className) == false) {
element.className += " " + className;
}
},
_removeClass: function(element, className) {
if (this._hasClass(element, className)) {
element.classList.remove(className);
var classAttributeValue = element.getAttribute('class');
if (!!! classAttributeValue) { // remove class attribute if no class is left to restore original html
element.removeAttribute('class');
}
}
},
/* Text Commands */
undo: function() {
this._executeCommand('undo', null);
},
redo: function() {
this._executeCommand('redo', null);
},
setBold: function() {
this._executeCommand('bold', null);
},
setItalic: function() {
this._executeCommand('italic', null);
},
setUnderline: function() {
this._executeCommand('underline', null);
},
setSubscript: function() {
this._executeCommand('subscript', null);
},
setSuperscript: function() {
this._executeCommand('superscript', null);
},
setStrikeThrough: function() {
this._executeCommand('strikeThrough', null);
},
setTextColor: function(color) {
this._executeStyleCommand('foreColor', color);
},
setTextBackgroundColor: function(color) {
if(color == 'rgba(0, 0, 0, 0)') { // resetting backColor does not work with any color value (whether #00000000 nor rgba(0, 0, 0, 0)), we have to pass 'inherit'. Thanks to https://stackoverflow.com/a/7071465 for pointing this out to me
this._executeStyleCommand('backColor', 'inherit');
}
else {
this._executeStyleCommand('backColor', color);
}
},
setFontName: function(fontName) {
this._executeCommand("fontName", fontName);
},
setFontSize: function(fontSize) {
this._executeCommand("fontSize", fontSize);
},
setHeading: function(heading) {
this._executeCommand('formatBlock', '<h'+heading+'>');
},
setFormattingToParagraph: function() {
this._executeCommand('formatBlock', '<p>');
},
setPreformat: function() {
this._executeCommand('formatBlock', '<pre>');
},
setBlockQuote: function() {
this._executeCommand('formatBlock', '<blockquote>');
},
removeFormat: function() {
this._executeCommand('removeFormat', null);
},
setJustifyLeft: function() {
this._executeCommand('justifyLeft', null);
},
setJustifyCenter: function() {
this._executeCommand('justifyCenter', null);
},
setJustifyRight: function() {
this._executeCommand('justifyRight', null);
},
setJustifyFull: function() {
this._executeCommand('justifyFull', null);
},
setIndent: function() {
this._executeCommand('indent', null);
},
setOutdent: function() {
this._executeCommand('outdent', null);
},
insertBulletList: function() {
this._executeCommand('insertUnorderedList', null);
},
insertNumberedList: function() {
this._executeCommand('insertOrderedList', null);
},
/* Insert elements */
insertLink: function(url, title) {
this._restoreRange();
var sel = document.getSelection();
if (sel.toString().length == 0) {
this._insertHtml("<a href='"+url+"'>"+title+"</a>");
}
else if (sel.rangeCount) {
var el = document.createElement("a");
el.setAttribute("href", url);
el.setAttribute("title", title);
var range = sel.getRangeAt(0).cloneRange();
range.surroundContents(el);
sel.removeAllRanges();
sel.addRange(range);
this._updateEditorState();
}
},
insertImage: function(url, alt, width, height, rotation) {
var imageElement = document.createElement('img');
imageElement.setAttribute('src', url);
if(alt) {
imageElement.setAttribute('alt', alt);
}
if(width) {
imageElement.setAttribute('width', width);
}
if(height) {
imageElement.setAttribute('height', height);
}
if(this._isImageResizingEnabled) {
imageElement.setAttribute('class', resizableImageClass);
}
if(rotation) {
this._setImageRotation(imageElement, rotation);
}
this._insertHtml(imageElement.outerHTML);
},
_setImageRotation: function(imageElement, rotation) {
if(rotation == 90) {
this._addClass(imageElement, 'rotate90deg');
}
else if(rotation == 180) {
this._addClass(imageElement, 'rotate180deg');
}
else if(rotation == 270) {
this._addClass(imageElement, 'rotate270deg');
}
},
insertCheckbox: function(text) {
var editor = this;
var html = '<input type="checkbox" name="'+ text +'" value="'+ text +'" onclick="editor._checkboxClicked(this)"/> &nbsp;';
this._insertHtml(html);
},
_checkboxClicked: function(clickedCheckbox) {
// incredible, checked attribute doesn't get set in html, see issue https://github.com/dankito/RichTextEditor/issues/24
if (clickedCheckbox.checked) {
clickedCheckbox.setAttribute('checked', 'checked');
}
else {
clickedCheckbox.removeAttribute('checked');
}
this._updateEditorState();
},
insertHtml: function(encodedHtml) {
var html = this._decodeHtml(encodedHtml);
this._insertHtml(html);
},
_insertHtml: function(html) {
this._backupRange();
this._restoreRange();
document.execCommand('insertHTML', false, html);
if(this._isImageResizingEnabled) {
this.makeImagesResizeable();
}
this._updateEditorState();
},
/* Editor default settings */
setBaseTextColor: function(color) {
this._textField.style.color = color;
},
setBaseFontFamily: function(fontFamily) {
this._textField.style.fontFamily = fontFamily;
},
setBaseFontSize: function(size) {
this._textField.style.fontSize = size;
},
setPadding: function(left, top, right, bottom) {
this._textField.style.paddingLeft = left;
this._textField.style.paddingTop = top;
this._textField.style.paddingRight = right;
this._textField.style.paddingBottom = bottom;
},
// TODO: is this one ever user?
setBackgroundColor: function(color) {
document.body.style.backgroundColor = color;
},
setBackgroundImage: function(image) {
this._textField.style.backgroundImage = image;
},
setWidth: function(size) {
this._textField.style.minWidth = size; // TODO: why did i use minWidth here but height (not minHeight) below?
},
setHeight: function(size) {
this._textField.style.height = size;
},
setTextAlign: function(align) {
this._textField.style.textAlign = align;
},
setVerticalAlign: function(align) {
this._textField.style.verticalAlign = align;
},
setPlaceholder: function(placeholder) {
this._textField.setAttribute("placeholder", placeholder);
},
setInputEnabled: function(inputEnabled) {
this._textField.contentEditable = String(inputEnabled);
if(inputEnabled) { // TODO: may interferes with _isImageResizingEnabled
this.makeImagesResizeable();
}
else {
this.disableImageResizing();
}
},
focus: function() {
var range = document.createRange();
range.selectNodeContents(this._textField);
range.collapse(false);
var selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
this._textField.focus();
},
blurFocus: function() {
this._textField.blur();
},
_executeStyleCommand: function(command, parameter) {
this._executeCommand("styleWithCSS", null, true);
this._executeCommand(command, parameter);
this._executeCommand("styleWithCSS", null, false);
},
_executeCommand: function(command, parameter) {
document.execCommand(command, false, parameter);
this._updateEditorState();
},
_updateEditorState: function() {
var html = this._getHtmlWithoutInternalModifications();
var didHtmlChange = (this._htmlSetByApplication != null && this._htmlSetByApplication != html) || // html set by application changed
(this._htmlSetByApplication == null && html != EditorDefaultHtml); // or if html not set by application: default html changed
if (typeof editorCallback !== 'undefined') { // in most applications like in the JavaFX app changing window.location.href doesn't work -> tell them via callback that editor state changed
editorCallback.updateEditorState(didHtmlChange) // these applications determine editor state manually
}
else if (this._useWindowLocationForEditorStateChangedCallback) { // Android can handle changes to windows.location -> communicate editor changes via a self defined protocol name
var commandStates = this._determineCommandStates();
var editorState = {
'didHtmlChange': didHtmlChange,
'html': html, // TODO: remove in upcoming versions
'commandStates': commandStates
};
window.location.href = "editor-state-changed-callback://" + encodeURIComponent(JSON.stringify(editorState));
}
},
_determineCommandStates: function() {
var commandStates = {};
this._determineStateForCommand('undo', commandStates);
this._determineStateForCommand('redo', commandStates);
this._determineStateForCommand('bold', commandStates);
this._determineStateForCommand('italic', commandStates);
this._determineStateForCommand('underline', commandStates);
this._determineStateForCommand('subscript', commandStates);
this._determineStateForCommand('superscript', commandStates);
this._determineStateForCommand('strikeThrough', commandStates);
this._determineStateForCommand('foreColor', commandStates);
this._determineStateForCommand('backColor', commandStates);
this._determineStateForCommand('fontName', commandStates);
this._determineStateForCommand('fontSize', commandStates);
this._determineStateForCommand('formatBlock', commandStates);
this._determineStateForCommand('removeFormat', commandStates);
this._determineStateForCommand('justifyLeft', commandStates);
this._determineStateForCommand('justifyCenter', commandStates);
this._determineStateForCommand('justifyRight', commandStates);
this._determineStateForCommand('justifyFull', commandStates);
this._determineStateForCommand('indent', commandStates);
this._determineStateForCommand('outdent', commandStates);
this._determineStateForCommand('insertUnorderedList', commandStates);
this._determineStateForCommand('insertOrderedList', commandStates);
this._determineStateForCommand('insertHorizontalRule', commandStates);
this._determineStateForCommand('insertHTML', commandStates);
return commandStates;
},
_determineStateForCommand: function(command, commandStates) {
commandStates[command.toUpperCase()] = {
'executable': document.queryCommandEnabled(command),
'value': document.queryCommandValue(command)
}
},
_backupRange: function(){
var selection = window.getSelection();
if(selection.rangeCount > 0) {
var range = selection.getRangeAt(0);
this._currentSelection = {
"startContainer": range.startContainer,
"startOffset": range.startOffset,
"endContainer": range.endContainer,
"endOffset": range.endOffset
};
}
},
_restoreRange: function(){
var selection = window.getSelection();
selection.removeAllRanges();
var range = document.createRange();
range.setStart(this._currentSelection.startContainer, this._currentSelection.startOffset);
range.setEnd(this._currentSelection.endContainer, this._currentSelection.endOffset);
selection.addRange(range);
},
}
editor.init();

106
assets/editor/style.css Normal file
View File

@@ -0,0 +1,106 @@
/**
* Copyright (C) 2017 Wasabeef
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@charset "UTF-8";
html {
height: 100%;
}
body {
overflow: scroll;
display: table;
table-layout: fixed;
width: 100%;
min-height:100%;
}
#editor {
display: table-cell;
outline: 0px solid transparent;
background-repeat: no-repeat;
background-position: center;
background-size: cover;
}
#editor[placeholder]:empty:not(:focus):before {
content: attr(placeholder);
opacity: .5;
}
p {
margin-top: 0;
margin-bottom: 8px;
-webkit-margin-before: 0em;
}
// Thanks to Jonathan B (https://stackoverflow.com/a/23501094) for figuring that out
sup {
vertical-align: super;
}
sub {
vertical-align: sub;
}
img {
max-width: 100%;
object-fit: contain;
}
/* change this when changing the class name of resizableImageClass in rich_text_editor.js */
img.resizable {
border: 1px solid gray;
touch-action: none;
/* This is suggested by the doc, but I'm not sure that it is required when there is no large borders around the image. */
/* This makes things *much* easier */
/* box-sizing: border-box;*/
}
.rotate90deg {
-webkit-transform: rotate(90deg);
-moz-transform: rotate(90deg);
-o-transform: rotate(90deg);
-ms-transform: rotate(90deg);
transform: rotate(90deg);
}
.rotate180deg {
-webkit-transform: rotate(180deg);
-moz-transform: rotate(180deg);
-o-transform: rotate(180deg);
-ms-transform: rotate(180deg);
transform: rotate(180deg);
}
.rotate270deg {
-webkit-transform: rotate(270deg);
-moz-transform: rotate(270deg);
-o-transform: rotate(270deg);
-ms-transform: rotate(270deg);
transform: rotate(270deg);
}