[evolution/wip/mcrha/webkit-jsc-api] Partially implement magic-links
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution/wip/mcrha/webkit-jsc-api] Partially implement magic-links
- Date: Wed, 4 Dec 2019 18:04:57 +0000 (UTC)
commit cfe018f8a6496452e6e3ca2439bd356df06147d5
Author: Milan Crha <mcrha redhat com>
Date: Wed Dec 4 19:06:17 2019 +0100
Partially implement magic-links
data/webkit/e-editor.js | 112 +++++++++++++
data/webkit/e-selection.js | 4 +-
data/webkit/e-undo-redo.js | 7 +-
src/modules/webkit-editor/e-webkit-editor.c | 177 ++++++++++++++++++++-
.../web-extension/e-editor-web-extension.c | 58 +++++++
5 files changed, 352 insertions(+), 6 deletions(-)
---
diff --git a/data/webkit/e-editor.js b/data/webkit/e-editor.js
index d207486ba1..d1438788bd 100644
--- a/data/webkit/e-editor.js
+++ b/data/webkit/e-editor.js
@@ -21,6 +21,14 @@
public functions start with upper-case letter. */
var EvoEditor = {
+ // stephenhay from https://mathiasbynens.be/demo/url-regex
+ URL_PATTERN : "((?:(?:(?:" + "news|telnet|nntp|file|https?|s?ftp|webcal|localhost|ssh" +
")\\:\\/\\/)|(?:www\\.|ftp\\.))[^\\s\\/\\$\\.\\?#].[^\\s]*+)",
+ // from camel-url-scanner.c
+ URL_INVALID_TRAILING_CHARS : ",.:;?!-|}])\">",
+ // http://www.w3.org/TR/html5/forms.html#valid-e-mail-address
+ EMAIL_PATTERN : "[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}" +
+ "[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*+",
+
E_CONTENT_EDITOR_ALIGNMENT_NONE : -1,
E_CONTENT_EDITOR_ALIGNMENT_LEFT : 0,
E_CONTENT_EDITOR_ALIGNMENT_CENTER : 1,
@@ -57,6 +65,9 @@ var EvoEditor = {
TEXT_INDENT_SIZE : 3, // in characters
NORMAL_PARAGRAPH_WIDTH : 71,
+ MAGIC_LINKS : true,
+ MAGIC_SMILEYS : false,
+ UNICODE_SMILEYS : false,
FORCE_NO : 0,
FORCE_YES : 1,
@@ -2066,6 +2077,107 @@ EvoEditor.UpdateThemeStyleSheet = function(css)
return EvoEditor.UpdateStyleSheet("x-evo-theme-sheet", css);
}
+EvoEditor.MaybeReplaceTextAfterInput = function(inputEvent, isWordDelim)
+{
+ var isInsertParagraph = inputEvent.inputType == "insertParagraph";
+
+ if ((!isInsertParagraph && inputEvent.inputType != "insertText") ||
+ (!(EvoEditor.MAGIC_LINKS && (isWordDelim || isInsertParagraph)) &&
+ !EvoEditor.MAGIC_SMILEYS)) {
+ return;
+ }
+
+ var selection = document.getSelection();
+
+ if (!selection.isCollapsed || !selection.baseNode)
+ return;
+
+ var baseNode = selection.baseNode, parentElem;
+
+ if (baseNode.nodeType != baseNode.ELEMENT_NODE) {
+ parentElem = baseNode.parentElement;
+
+ if (!parentElem)
+ return;
+ } else {
+ parentElem = baseNode;
+ }
+
+ if (isInsertParagraph) {
+ parentElem = parentElem.previousElementSibling;
+
+ if (!parentElem)
+ return;
+
+ baseNode = parentElem.lastChild;
+
+ if (!baseNode || baseNode.nodeType != baseNode.TEXT_NODE)
+ return;
+ }
+
+ if (baseNode.nodeValue == "")
+ return;
+
+ var canLinks;
+
+ canLinks = EvoEditor.MAGIC_LINKS && (isWordDelim || isInsertParagraph);
+
+ if (canLinks) {
+ var tmpNode;
+
+ for (tmpNode = baseNode; tmpNode && tmpNode.tagName != "BODY"; tmpNode =
tmpNode.parentElement) {
+ if (tmpNode.tagName == "A") {
+ canLinks = false;
+ break;
+ }
+ }
+ }
+
+ var text = baseNode.nodeValue, selectionUpdater, covered = false;
+
+ selectionUpdater = EvoSelection.CreateUpdaterObject();
+
+ if (canLinks) {
+ var isEmail = text.search("@") >= 0, match;
+
+ // the replace call below replaces (0xA0) with regular space
+ match = EvoEditor.findPattern(text.replace(/ /g, " "), isEmail ? EvoEditor.EMAIL_PATTERN :
EvoEditor.URL_PATTERN);
+ if (match) {
+ var url = text.substring(match.start, match.end), node;
+
+ EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "magicLink",
baseNode.parentElement, baseNode.parentElement,
+ EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+
+ try {
+ covered = true;
+
+ baseNode.splitText(match.end);
+ baseNode.splitText(match.start);
+
+ baseNode = baseNode.nextSibling;
+
+ if (isEmail)
+ url = "mailto:" + url;
+ else if (url.startsWith("www."))
+ url = "http://" + url;
+
+ node = document.createElement("A");
+ node.href = url;
+
+ baseNode.parentElement.insertBefore(node, baseNode);
+ node.appendChild(baseNode);
+ } finally {
+ EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "magicLink");
+ }
+ }
+ }
+
+ if (!covered && EvoEditor.MAGIC_SMILEYS) {
+ }
+
+ selectionUpdater.restore();
+}
+
document.onload = EvoEditor.initializeContent;
document.onselectionchange = function() {
diff --git a/data/webkit/e-selection.js b/data/webkit/e-selection.js
index 5513e63f0c..bb880a11c9 100644
--- a/data/webkit/e-selection.js
+++ b/data/webkit/e-selection.js
@@ -382,11 +382,11 @@ EvoSelection.CreateUpdaterObject = function()
obj.selectionBefore = EvoSelection.Store(document);
obj.selectionBaseNode = document.getSelection().baseNode;
- obj.selectionBaseOffset = document.getSelection().baseOffset;
+ obj.selectionBaseOffset = document.getSelection().baseOffset +
EvoSelection.GetOverallTextOffset(obj.selectionBaseNode);
if (!document.getSelection().isCollapsed) {
obj.selectionExtentNode = document.getSelection().extentNode;
- obj.selectionExtentOffset = document.getSelection().extentOffset;
+ obj.selectionExtentOffset = document.getSelection().extentOffset +
EvoSelection.GetOverallTextOffset(obj.selectionExtentNode);
}
return obj;
diff --git a/data/webkit/e-undo-redo.js b/data/webkit/e-undo-redo.js
index 3f22f36d61..1024d419d6 100644
--- a/data/webkit/e-undo-redo.js
+++ b/data/webkit/e-undo-redo.js
@@ -452,14 +452,17 @@ EvoUndoRedo.before_input_cb = function(inputEvent)
EvoUndoRedo.input_cb = function(inputEvent)
{
+ var isWordDelim = EvoUndoRedo.isWordDelimEvent(inputEvent);
+
if (EvoUndoRedo.disabled) {
EvoEditor.EmitContentChanged();
+ EvoEditor.MaybeReplaceTextAfterInput(inputEvent, isWordDelim);
return;
}
var opType = inputEvent.inputType;
- if (EvoUndoRedo.isWordDelimEvent(inputEvent))
+ if (isWordDelim)
opType += "::WordDelim";
if (EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_EVENT, opType)) {
@@ -477,6 +480,8 @@ EvoUndoRedo.input_cb = function(inputEvent)
if (opType == "insertFromDrop")
EvoUndoRedo.dropTarget = null;
+
+ EvoEditor.MaybeReplaceTextAfterInput(inputEvent, isWordDelim);
}
EvoUndoRedo.drop_cb = function(event)
diff --git a/src/modules/webkit-editor/e-webkit-editor.c b/src/modules/webkit-editor/e-webkit-editor.c
index 4f9c5a4bb1..145d582e71 100644
--- a/src/modules/webkit-editor/e-webkit-editor.c
+++ b/src/modules/webkit-editor/e-webkit-editor.c
@@ -65,7 +65,10 @@ enum {
PROP_SUPERSCRIPT,
PROP_UNDERLINE,
- PROP_NORMAL_PARAGRAPH_WIDTH
+ PROP_NORMAL_PARAGRAPH_WIDTH,
+ PROP_MAGIC_LINKS,
+ PROP_MAGIC_SMILEYS,
+ PROP_UNICODE_SMILEYS
};
struct _EWebKitEditorPrivate {
@@ -112,6 +115,9 @@ struct _EWebKitEditorPrivate {
guint font_size;
gint normal_paragraph_width;
+ gboolean magic_links;
+ gboolean magic_smileys;
+ gboolean unicode_smileys;
EContentEditorBlockFormat block_format;
EContentEditorAlignment alignment;
@@ -5145,6 +5151,81 @@ webkit_editor_get_normal_paragraph_width (EWebKitEditor *wk_editor)
return wk_editor->priv->normal_paragraph_width;
}
+static void
+webkit_editor_set_magic_links (EWebKitEditor *wk_editor,
+ gboolean value)
+{
+ g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+
+ if ((wk_editor->priv->magic_links ? 1 : 0) != (value ? 1 : 0)) {
+ wk_editor->priv->magic_links = value;
+
+ e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+ "EvoEditor.MAGIC_LINKS = %x;",
+ value);
+
+ g_object_notify (G_OBJECT (wk_editor), "magic-links");
+ }
+}
+
+static gboolean
+webkit_editor_get_magic_links (EWebKitEditor *wk_editor)
+{
+ g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE);
+
+ return wk_editor->priv->magic_links;
+}
+
+static void
+webkit_editor_set_magic_smileys (EWebKitEditor *wk_editor,
+ gboolean value)
+{
+ g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+
+ if ((wk_editor->priv->magic_smileys ? 1 : 0) != (value ? 1 : 0)) {
+ wk_editor->priv->magic_smileys = value;
+
+ e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+ "EvoEditor.MAGIC_SMILEYS = %x;",
+ value);
+
+ g_object_notify (G_OBJECT (wk_editor), "magic-smileys");
+ }
+}
+
+static gboolean
+webkit_editor_get_magic_smileys (EWebKitEditor *wk_editor)
+{
+ g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE);
+
+ return wk_editor->priv->magic_smileys;
+}
+
+static void
+webkit_editor_set_unicode_smileys (EWebKitEditor *wk_editor,
+ gboolean value)
+{
+ g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+
+ if ((wk_editor->priv->unicode_smileys ? 1 : 0) != (value ? 1 : 0)) {
+ wk_editor->priv->unicode_smileys = value;
+
+ e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+ "EvoEditor.UNICODE_SMILEYS = %x;",
+ value);
+
+ g_object_notify (G_OBJECT (wk_editor), "unicide-smileys");
+ }
+}
+
+static gboolean
+webkit_editor_get_unicode_smileys (EWebKitEditor *wk_editor)
+{
+ g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE);
+
+ return wk_editor->priv->unicode_smileys;
+}
+
static void
e_webkit_editor_initialize_web_extensions_cb (WebKitWebContext *web_context,
gpointer user_data)
@@ -5214,10 +5295,27 @@ webkit_editor_constructed (GObject *object)
webkit_settings_set_enable_developer_extras (web_settings, e_util_get_webkit_developer_mode_enabled
());
settings = e_util_ref_settings ("org.gnome.evolution.mail");
+
g_settings_bind (
settings, "composer-word-wrap-length",
wk_editor, "normal-paragraph-width",
G_SETTINGS_BIND_GET);
+
+ g_settings_bind (
+ settings, "composer-magic-links",
+ wk_editor, "magic-links",
+ G_SETTINGS_BIND_GET);
+
+ g_settings_bind (
+ settings, "composer-magic-smileys",
+ wk_editor, "magic-smileys",
+ G_SETTINGS_BIND_GET);
+
+ g_settings_bind (
+ settings, "composer-unicode-smileys",
+ wk_editor, "unicode-smileys",
+ G_SETTINGS_BIND_GET);
+
g_object_unref (settings);
e_webkit_editor_load_data (wk_editor, "");
@@ -5406,6 +5504,24 @@ webkit_editor_set_property (GObject *object,
g_value_get_int (value));
return;
+ case PROP_MAGIC_LINKS:
+ webkit_editor_set_magic_links (
+ E_WEBKIT_EDITOR (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_MAGIC_SMILEYS:
+ webkit_editor_set_magic_smileys (
+ E_WEBKIT_EDITOR (object),
+ g_value_get_boolean (value));
+ return;
+
+ case PROP_UNICODE_SMILEYS:
+ webkit_editor_set_unicode_smileys (
+ E_WEBKIT_EDITOR (object),
+ g_value_get_boolean (value));
+ return;
+
case PROP_ALIGNMENT:
webkit_editor_set_alignment (
E_WEBKIT_EDITOR (object),
@@ -5584,6 +5700,21 @@ webkit_editor_get_property (GObject *object,
webkit_editor_get_normal_paragraph_width (E_WEBKIT_EDITOR (object)));
return;
+ case PROP_MAGIC_LINKS:
+ g_value_set_boolean (value,
+ webkit_editor_get_magic_links (E_WEBKIT_EDITOR (object)));
+ return;
+
+ case PROP_MAGIC_SMILEYS:
+ g_value_set_boolean (value,
+ webkit_editor_get_magic_smileys (E_WEBKIT_EDITOR (object)));
+ return;
+
+ case PROP_UNICODE_SMILEYS:
+ g_value_set_boolean (value,
+ webkit_editor_get_unicode_smileys (E_WEBKIT_EDITOR (object)));
+ return;
+
case PROP_ALIGNMENT:
g_value_set_enum (
value,
@@ -6452,7 +6583,43 @@ e_webkit_editor_class_init (EWebKitEditorClass *class)
NULL,
G_MININT32,
G_MAXINT32,
- 71, /* Should be the same as e-editor.js:EvoEditor.NORMAL_PARAGRAPH_WIDTH */
+ 71, /* Should be the same as e-editor.js:EvoEditor.NORMAL_PARAGRAPH_WIDTH and in the
init()*/
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_MAGIC_LINKS,
+ g_param_spec_boolean (
+ "magic-links",
+ NULL,
+ NULL,
+ TRUE, /* Should be the same as e-editor.js:EvoEditor.MAGIC_LINKS and in the init() */
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_MAGIC_SMILEYS,
+ g_param_spec_boolean (
+ "magic-smileys",
+ NULL,
+ NULL,
+ FALSE, /* Should be the same as e-editor.js:EvoEditor.MAGIC_SMILEYS and in the init()
*/
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_UNICODE_SMILEYS,
+ g_param_spec_boolean (
+ "unicode-smileys",
+ NULL,
+ NULL,
+ FALSE, /* Should be the same as e-editor.js:EvoEditor.UNICODE_SMILEYS and in the
init() */
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT |
G_PARAM_STATIC_STRINGS));
@@ -6474,6 +6641,11 @@ e_webkit_editor_init (EWebKitEditor *wk_editor)
wk_editor->priv->old_settings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
(GDestroyNotify) g_variant_unref);
wk_editor->priv->visually_wrap_long_lines = FALSE;
+ wk_editor->priv->normal_paragraph_width = 71;
+ wk_editor->priv->magic_links = TRUE;
+ wk_editor->priv->magic_smileys = FALSE;
+ wk_editor->priv->unicode_smileys = FALSE;
+
g_signal_connect (
wk_editor, "load-changed",
G_CALLBACK (webkit_editor_load_changed_cb), NULL);
@@ -6578,7 +6750,6 @@ e_webkit_editor_init (EWebKitEditor *wk_editor)
wk_editor->priv->start_bottom = E_THREE_STATE_INCONSISTENT;
wk_editor->priv->top_signature = E_THREE_STATE_INCONSISTENT;
- wk_editor->priv->normal_paragraph_width = 71; /* Should be the same as
e-editor.js:EvoEditor.NORMAL_PARAGRAPH_WIDTH */
}
static void
diff --git a/src/modules/webkit-editor/web-extension/e-editor-web-extension.c
b/src/modules/webkit-editor/web-extension/e-editor-web-extension.c
index dbcb9c9dc5..497e53ee01 100644
--- a/src/modules/webkit-editor/web-extension/e-editor-web-extension.c
+++ b/src/modules/webkit-editor/web-extension/e-editor-web-extension.c
@@ -164,6 +164,48 @@ load_javascript_file (JSCContext *jsc_context,
g_free (content);
}
+/* Returns 'null', when no match for the 'pattern' in 'text' found, otherwise
+ returns an 'object { start : nnn, end : nnn };' with the first longest pattern match. */
+static JSCValue *
+evo_editor_jsc_find_pattern (const gchar *text,
+ const gchar *pattern,
+ JSCContext *jsc_context)
+{
+ JSCValue *object = NULL;
+ GRegex *regex;
+
+ if (!text || !*text || !pattern || !*pattern)
+ return jsc_value_new_null (jsc_context);
+
+ regex = g_regex_new (pattern, 0, 0, NULL);
+ if (regex) {
+ GMatchInfo *match_info = NULL;
+ gint start = -1, end = -1;
+
+ if (g_regex_match_all (regex, text, G_REGEX_MATCH_NOTEMPTY, &match_info) &&
+ g_match_info_fetch_pos (match_info, 0, &start, &end) &&
+ start >= 0 && end >= 0) {
+ JSCValue *number;
+
+ object = jsc_value_new_object (jsc_context, NULL, NULL);
+
+ number = jsc_value_new_number (jsc_context, start);
+ jsc_value_object_set_property (object, "start", number);
+ g_clear_object (&number);
+
+ number = jsc_value_new_number (jsc_context, end);
+ jsc_value_object_set_property (object, "end", number);
+ g_clear_object (&number);
+ }
+
+ if (match_info)
+ g_match_info_free (match_info);
+ g_regex_unref (regex);
+ }
+
+ return object ? object : jsc_value_new_null (jsc_context);
+}
+
static void
window_object_cleared_cb (WebKitScriptWorld *world,
WebKitWebPage *page,
@@ -171,6 +213,7 @@ window_object_cleared_cb (WebKitScriptWorld *world,
gpointer user_data)
{
JSCContext *jsc_context;
+ JSCValue *jsc_editor;
/* Load the javascript files only to the main frame, not to the subframes */
if (!webkit_frame_is_main_frame (frame))
@@ -184,6 +227,21 @@ window_object_cleared_cb (WebKitScriptWorld *world,
load_javascript_file (jsc_context, "e-undo-redo.js");
load_javascript_file (jsc_context, "e-editor.js");
+ jsc_editor = jsc_context_get_value (jsc_context, "EvoEditor");
+
+ if (jsc_editor) {
+ JSCValue *jsc_function;
+
+ jsc_function = jsc_value_new_function (jsc_context, "findPattern",
+ G_CALLBACK (evo_editor_jsc_find_pattern), g_object_ref (jsc_context), g_object_unref,
+ JSC_TYPE_VALUE, 2, G_TYPE_STRING, G_TYPE_STRING);
+
+ jsc_value_object_set_property (jsc_editor, "findPattern", jsc_function);
+
+ g_clear_object (&jsc_function);
+ g_clear_object (&jsc_editor);
+ }
+
g_clear_object (&jsc_context);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]