mirror of
https://github.com/leanote/leanote-android.git
synced 2025-10-15 06:40:58 +00:00
add new web container, support format: bold, italic, blockquote, ordered list, bullet list, header, image
remaining problems - unfinished link format - format button’s status - output not equals to input - editor’s border
This commit is contained in:
187
app/src/main/assets/RichTextEditor/editor.html
Executable file
187
app/src/main/assets/RichTextEditor/editor.html
Executable file
@@ -0,0 +1,187 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>RichTextEditor</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0">
|
||||||
|
<script src="./tinymce/tinymce.min.js"></script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div contenteditable="true" id="title"></div>
|
||||||
|
<hr>
|
||||||
|
<form method="post">
|
||||||
|
<div id="content"></div>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
var titleDiv = document.getElementById('title');
|
||||||
|
var nativeCallbackHandler = {};
|
||||||
|
nativeCallbackHandler.onFormatChange = function (format, msg) {
|
||||||
|
console.log(format, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTitle() {
|
||||||
|
return titleDiv.innerHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTitle(title) {
|
||||||
|
return titleDiv.innerHTML = title;
|
||||||
|
}
|
||||||
|
tinymce.init({
|
||||||
|
selector: 'div#content',
|
||||||
|
remove_trailing_brs: false,
|
||||||
|
element_format: 'html',
|
||||||
|
allow_unsafe_link_target: true,
|
||||||
|
plugins: "lists",
|
||||||
|
// toolbar: false,
|
||||||
|
// menubar: false,
|
||||||
|
inline: true
|
||||||
|
});
|
||||||
|
|
||||||
|
function toggleBold() {
|
||||||
|
tinyMCE.editors[0].formatter.toggle('bold');
|
||||||
|
var currentState = tinyMCE.editors[0].formatter.match('bold');
|
||||||
|
nativeCallbackHandler.onFormatChange('bold', currentState);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleBlockquote() {
|
||||||
|
tinyMCE.editors[0].formatter.toggle('blockquote');
|
||||||
|
var currentState = tinyMCE.editors[0].formatter.match('blockquote');
|
||||||
|
nativeCallbackHandler.onFormatChange('blockquote', currentState);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleHeader() {
|
||||||
|
var currentHeader = getCurrentHeader();
|
||||||
|
if (currentHeader) {
|
||||||
|
var currentVal = parseInt(currentHeader.substr(1));
|
||||||
|
var newVal = (currentVal + 1) % 7;
|
||||||
|
if (newVal) {
|
||||||
|
tinyMCE.editors[0].formatter.apply('h' + newVal);
|
||||||
|
} else {
|
||||||
|
tinyMCE.editors[0].formatter.remove(currentHeader);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tinyMCE.editors[0].formatter.apply('h1');
|
||||||
|
}
|
||||||
|
currentHeader = getCurrentHeader();
|
||||||
|
nativeCallbackHandler.onFormatChange('header', currentHeader);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCurrentHeader() {
|
||||||
|
var intrestFormats = [
|
||||||
|
'h1',
|
||||||
|
'h2',
|
||||||
|
'h3',
|
||||||
|
'h4',
|
||||||
|
'h5',
|
||||||
|
'h6'
|
||||||
|
];
|
||||||
|
var result = tinyMCE.editors[0].formatter.matchAll(intrestFormats);
|
||||||
|
if (result) {
|
||||||
|
return result[0];
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleItalic() {
|
||||||
|
tinyMCE.editors[0].formatter.toggle('italic');
|
||||||
|
var currentState = tinyMCE.editors[0].formatter.match('italic');
|
||||||
|
nativeCallbackHandler.onFormatChange('italic', currentState);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleBulletList() {
|
||||||
|
tinyMCE.editors[0].execCommand("InsertUnorderedList", false)
|
||||||
|
var listState = getListState();
|
||||||
|
nativeCallbackHandler.onFormatChange('bullet', listState && listState.toLowerCase() === 'ul');
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleOrderedList() {
|
||||||
|
tinyMCE.editors[0].execCommand("InsertOrderedList", false)
|
||||||
|
var listState = getListState();
|
||||||
|
nativeCallbackHandler.onFormatChange('bullet', listState && listState.toLowerCase() === 'ol');
|
||||||
|
}
|
||||||
|
|
||||||
|
function insertImage(src) {
|
||||||
|
tinyMCE.editors[0].insertContent('<img src="' + src + '" alt=""/>');
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatLink(url) {
|
||||||
|
tinyMCE.editors[0].formatter.apply('link', {
|
||||||
|
href: url
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeLink(title, url) {
|
||||||
|
tinyMCE.editors[0].formatter.remove('link');
|
||||||
|
}
|
||||||
|
|
||||||
|
function enable() {
|
||||||
|
document.addEventListener("selectionchange", selectionChangeHandler, false);
|
||||||
|
document.removeEventListener("click", clickHandeler, false);
|
||||||
|
tinyMCE.editors[0].setMode('design');
|
||||||
|
titleDiv.setAttribute("contenteditable", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function disable() {
|
||||||
|
document.removeEventListener("selectionchange", selectionChangeHandler, false);
|
||||||
|
document.addEventListener("click", clickHandeler, false);
|
||||||
|
tinyMCE.editors[0].setMode('readonly');
|
||||||
|
titleDiv.setAttribute("contenteditable", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function clickHandeler(e) {
|
||||||
|
var target = e.target;
|
||||||
|
if (target.tagName === 'A') {
|
||||||
|
var link = target.getAttribute('href');
|
||||||
|
var title = target.innerHTML;
|
||||||
|
e.preventDefault();
|
||||||
|
nativeCallbackHandler.gotoLink(title, link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectionChangeHandler() {
|
||||||
|
var intrestFormats = ['bold',
|
||||||
|
'italic',
|
||||||
|
'h1',
|
||||||
|
'h2',
|
||||||
|
'h3',
|
||||||
|
'h4',
|
||||||
|
'h5',
|
||||||
|
'h6',
|
||||||
|
'blockquote',
|
||||||
|
'link'
|
||||||
|
];
|
||||||
|
var result = tinyMCE.editors[0].formatter.matchAll(intrestFormats);
|
||||||
|
listState = getListState();
|
||||||
|
if (listState) {
|
||||||
|
result.push(listState);
|
||||||
|
}
|
||||||
|
console.log(result);
|
||||||
|
nativeCallbackHandler.onCursorChanged(JSON.stringify(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getListState() {
|
||||||
|
var node = tinyMCE.editors[0].selection.getNode()
|
||||||
|
var parents = tinymce.dom.DomQuery(node).parents();
|
||||||
|
var lists = tinyMCE.util.Tools.grep(parents, isNodeList);
|
||||||
|
if (lists.length > 0) {
|
||||||
|
return lists[0].nodeName.toLowerCase();
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isNodeList(node) {
|
||||||
|
return node && (/^(OL|UL|DL)$/).test(node.nodeName) && isChildOfBody(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isChildOfBody(elm) {
|
||||||
|
return tinyMCE.editors[0].$.contains(tinyMCE.editors[0].getBody(), elm);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</html>
|
@@ -2,9 +2,7 @@ package org.houxg.leamonax.editor;
|
|||||||
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.webkit.WebChromeClient;
|
|
||||||
import android.webkit.WebView;
|
import android.webkit.WebView;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
@@ -34,9 +32,9 @@ public class RichTextEditor extends Editor implements OnJsEditorStateChangedList
|
|||||||
mWebView.setScrollBarStyle(SCROLLBARS_OUTSIDE_OVERLAY);
|
mWebView.setScrollBarStyle(SCROLLBARS_OUTSIDE_OVERLAY);
|
||||||
mWebView.getSettings().setJavaScriptEnabled(true);
|
mWebView.getSettings().setJavaScriptEnabled(true);
|
||||||
mWebView.setWebViewClient(new EditorClient());
|
mWebView.setWebViewClient(new EditorClient());
|
||||||
mWebView.setWebChromeClient(new WebChromeClient());
|
mWebView.setWebChromeClient(new EditorChromeClient());
|
||||||
mWebView.addJavascriptInterface(new JsCallbackHandler(this), JS_CALLBACK_HANDLER);
|
mWebView.addJavascriptInterface(new JsCallbackHandler(this), JS_CALLBACK_HANDLER);
|
||||||
mWebView.loadUrl("file:///android_asset/android-editor.html");
|
mWebView.loadUrl("file:///android_asset/RichTextEditor/editor.html");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void execJs(final String script) {
|
private void execJs(final String script) {
|
||||||
@@ -51,46 +49,42 @@ public class RichTextEditor extends Editor implements OnJsEditorStateChangedList
|
|||||||
@Override
|
@Override
|
||||||
public void setEditingEnabled(boolean enabled) {
|
public void setEditingEnabled(boolean enabled) {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
execJs("ZSSEditor.getField('zss_field_title').enableEditing();");
|
execJs("enable();");
|
||||||
execJs("ZSSEditor.getField('zss_field_content').enableEditing();");
|
|
||||||
} else {
|
} else {
|
||||||
execJs("ZSSEditor.getField('zss_field_title').disableEditing();");
|
execJs("disable()");
|
||||||
execJs("ZSSEditor.getField('zss_field_content').disableEditing();");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setTitle(String title) {
|
public void setTitle(String title) {
|
||||||
execJs(String.format(Locale.US, "ZSSEditor.getField('zss_field_title').setPlainText('%s');", HtmlUtils.escapeHtml(title)));
|
execJs(String.format(Locale.US, "setTitle('%s');", HtmlUtils.escapeHtml(title)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getTitle() {
|
public String getTitle() {
|
||||||
return HtmlUtils.unescapeHtml(new JsRunner().get(mWebView, "ZSSEditor.getField('zss_field_title').getHTML();"));
|
return HtmlUtils.unescapeHtml(new JsRunner().get(mWebView, "getTitle();"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setContent(String content) {
|
public void setContent(String content) {
|
||||||
execJs(String.format(Locale.US, "ZSSEditor.getField('zss_field_content').setHTML('%s');", HtmlUtils.escapeHtml(content)));
|
execJs(String.format(Locale.US, "tinyMCE.editors[0].setContent('%s');", HtmlUtils.escapeHtml(content)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getContent() {
|
public String getContent() {
|
||||||
String content = HtmlUtils.unescapeHtml(new JsRunner().get(mWebView, "ZSSEditor.getField('zss_field_content').getHTML();"));
|
String content = HtmlUtils.unescapeHtml(new JsRunner().get(mWebView, "tinyMCE.editors[0].getContent();"));
|
||||||
if (!TextUtils.isEmpty(content)) {
|
content = content.replaceAll("\\n", "");
|
||||||
content = appendPTag(content);
|
|
||||||
}
|
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void insertImage(String title, String url) {
|
public void insertImage(String title, String url) {
|
||||||
execJs(String.format(Locale.US, "ZSSEditor.insertImage('%s', '%s');", url, title));
|
execJs(String.format(Locale.US, "insertImage('%s');", url));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void insertLink(String title, String url) {
|
public void insertLink(String title, String url) {
|
||||||
execJs(String.format(Locale.US, "ZSSEditor.insertLink('%s', '%s');", url, title));
|
execJs(String.format(Locale.US, "formatLink('%s');", url));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -100,60 +94,44 @@ public class RichTextEditor extends Editor implements OnJsEditorStateChangedList
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void redo() {
|
public void redo() {
|
||||||
execJs("ZSSEditor.redo();");
|
execJs("tinyMCE.editors[0].undoManager.redo();");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void undo() {
|
public void undo() {
|
||||||
execJs("ZSSEditor.undo();");
|
execJs("tinyMCE.editors[0].undoManager.undo();");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void toggleOrderList() {
|
public void toggleOrderList() {
|
||||||
execJs("ZSSEditor.setOrderedList();");
|
execJs("toggleOrderedList();");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void toggleUnorderList() {
|
public void toggleUnorderList() {
|
||||||
execJs("ZSSEditor.setUnorderedList();");
|
execJs("toggleBulletList();");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void toggleBold() {
|
public void toggleBold() {
|
||||||
execJs("ZSSEditor.setBold();");
|
execJs("toggleBold();");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void toggleItalic() {
|
public void toggleItalic() {
|
||||||
execJs("ZSSEditor.setItalic();");
|
execJs("toggleItalic();");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void toggleQuote() {
|
public void toggleQuote() {
|
||||||
execJs("ZSSEditor.setBlockquote();");
|
execJs("toggleBlockquote();");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void toggleHeading() {
|
public void toggleHeading() {
|
||||||
execJs("ZSSEditor.setHeading();");
|
execJs("toggleHeader();");
|
||||||
}
|
}
|
||||||
|
|
||||||
private String appendPTag(String source) {
|
|
||||||
String[] segments = source.split("\n\n");
|
|
||||||
StringBuilder contentBuilder = new StringBuilder();
|
|
||||||
if (segments.length > 0) {
|
|
||||||
for (String segment : segments) {
|
|
||||||
contentBuilder.append("<p>");
|
|
||||||
contentBuilder.append(segment);
|
|
||||||
contentBuilder.append("</p>");
|
|
||||||
}
|
|
||||||
return contentBuilder.toString();
|
|
||||||
} else {
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDomLoaded() {
|
public void onDomLoaded() {
|
||||||
execJs("ZSSEditor.getField('zss_field_content').setMultiline('true');");
|
execJs("ZSSEditor.getField('zss_field_content').setMultiline('true');");
|
||||||
|
@@ -284,7 +284,8 @@ public class Note extends BaseModel implements Serializable {
|
|||||||
private boolean isChanged(String message, Object l, Object r) {
|
private boolean isChanged(String message, Object l, Object r) {
|
||||||
boolean isEqual = l.equals(r);
|
boolean isEqual = l.equals(r);
|
||||||
if (!isEqual) {
|
if (!isEqual) {
|
||||||
Log.i("Note", message + " changed, origin=" + l + ", modified=" + r);
|
Log.i("Note", message + " changed, origin =" + l);
|
||||||
|
Log.i("Note", message + " changed, modified=" + r);
|
||||||
}
|
}
|
||||||
return !isEqual;
|
return !isEqual;
|
||||||
}
|
}
|
||||||
|
@@ -215,7 +215,7 @@ public class NoteEditActivity extends BaseActivity implements EditorFragment.Edi
|
|||||||
if (!subscriber.isUnsubscribed()) {
|
if (!subscriber.isUnsubscribed()) {
|
||||||
updateNote();
|
updateNote();
|
||||||
if (mModified.note.isDirty()
|
if (mModified.note.isDirty()
|
||||||
|| mModified.note.hasChanges(mOriginal.note)
|
|| mOriginal.note.hasChanges(mModified.note)
|
||||||
|| isLocalNote(mModified.note)
|
|| isLocalNote(mModified.note)
|
||||||
|| isTitleContentEmpty(mModified.note)
|
|| isTitleContentEmpty(mModified.note)
|
||||||
|| !CollectionUtils.isTheSame(mOriginal.tags, mModified.tags)) {
|
|| !CollectionUtils.isTheSame(mOriginal.tags, mModified.tags)) {
|
||||||
|
Reference in New Issue
Block a user