mirror of
https://github.com/leanote/leanote-android.git
synced 2025-10-15 14:51:04 +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.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
@@ -34,9 +32,9 @@ public class RichTextEditor extends Editor implements OnJsEditorStateChangedList
|
||||
mWebView.setScrollBarStyle(SCROLLBARS_OUTSIDE_OVERLAY);
|
||||
mWebView.getSettings().setJavaScriptEnabled(true);
|
||||
mWebView.setWebViewClient(new EditorClient());
|
||||
mWebView.setWebChromeClient(new WebChromeClient());
|
||||
mWebView.setWebChromeClient(new EditorChromeClient());
|
||||
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) {
|
||||
@@ -51,46 +49,42 @@ public class RichTextEditor extends Editor implements OnJsEditorStateChangedList
|
||||
@Override
|
||||
public void setEditingEnabled(boolean enabled) {
|
||||
if (enabled) {
|
||||
execJs("ZSSEditor.getField('zss_field_title').enableEditing();");
|
||||
execJs("ZSSEditor.getField('zss_field_content').enableEditing();");
|
||||
execJs("enable();");
|
||||
} else {
|
||||
execJs("ZSSEditor.getField('zss_field_title').disableEditing();");
|
||||
execJs("ZSSEditor.getField('zss_field_content').disableEditing();");
|
||||
execJs("disable()");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
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
|
||||
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
|
||||
public String getContent() {
|
||||
String content = HtmlUtils.unescapeHtml(new JsRunner().get(mWebView, "ZSSEditor.getField('zss_field_content').getHTML();"));
|
||||
if (!TextUtils.isEmpty(content)) {
|
||||
content = appendPTag(content);
|
||||
}
|
||||
String content = HtmlUtils.unescapeHtml(new JsRunner().get(mWebView, "tinyMCE.editors[0].getContent();"));
|
||||
content = content.replaceAll("\\n", "");
|
||||
return content;
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
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
|
||||
@@ -100,60 +94,44 @@ public class RichTextEditor extends Editor implements OnJsEditorStateChangedList
|
||||
|
||||
@Override
|
||||
public void redo() {
|
||||
execJs("ZSSEditor.redo();");
|
||||
execJs("tinyMCE.editors[0].undoManager.redo();");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undo() {
|
||||
execJs("ZSSEditor.undo();");
|
||||
execJs("tinyMCE.editors[0].undoManager.undo();");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toggleOrderList() {
|
||||
execJs("ZSSEditor.setOrderedList();");
|
||||
execJs("toggleOrderedList();");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toggleUnorderList() {
|
||||
execJs("ZSSEditor.setUnorderedList();");
|
||||
execJs("toggleBulletList();");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toggleBold() {
|
||||
execJs("ZSSEditor.setBold();");
|
||||
execJs("toggleBold();");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toggleItalic() {
|
||||
execJs("ZSSEditor.setItalic();");
|
||||
execJs("toggleItalic();");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toggleQuote() {
|
||||
execJs("ZSSEditor.setBlockquote();");
|
||||
execJs("toggleBlockquote();");
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
public void onDomLoaded() {
|
||||
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) {
|
||||
boolean isEqual = l.equals(r);
|
||||
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;
|
||||
}
|
||||
|
@@ -215,7 +215,7 @@ public class NoteEditActivity extends BaseActivity implements EditorFragment.Edi
|
||||
if (!subscriber.isUnsubscribed()) {
|
||||
updateNote();
|
||||
if (mModified.note.isDirty()
|
||||
|| mModified.note.hasChanges(mOriginal.note)
|
||||
|| mOriginal.note.hasChanges(mModified.note)
|
||||
|| isLocalNote(mModified.note)
|
||||
|| isTitleContentEmpty(mModified.note)
|
||||
|| !CollectionUtils.isTheSame(mOriginal.tags, mModified.tags)) {
|
||||
|
Reference in New Issue
Block a user