[evolution/wip/mcrha/webkit-jsc-api] Implement "Magic smileys"
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution/wip/mcrha/webkit-jsc-api] Implement "Magic smileys"
- Date: Fri, 31 Jan 2020 13:20:23 +0000 (UTC)
commit fd53cf02728efe024c87b3d5bba8346c86f34ed3
Author: Milan Crha <mcrha redhat com>
Date: Fri Jan 31 14:21:40 2020 +0100
Implement "Magic smileys"
data/webkit/e-editor.js | 222 ++++++++++++++++++---
.../web-extension/e-editor-web-extension.c | 64 ++++++
2 files changed, 254 insertions(+), 32 deletions(-)
---
diff --git a/data/webkit/e-editor.js b/data/webkit/e-editor.js
index f278477e9a..fc0badc89d 100644
--- a/data/webkit/e-editor.js
+++ b/data/webkit/e-editor.js
@@ -2243,6 +2243,119 @@ EvoEditor.UpdateThemeStyleSheet = function(css)
return EvoEditor.UpdateStyleSheet("x-evo-theme-sheet", css);
}
+EvoEditor.findSmileys = function(text, unicodeSmileys)
+{
+ /* Based on original use_pictograms() from GtkHTML */
+ var emoticons_chars = [
+ /* 0 */ "D", "O", ")", "(", "|", "/", "P", "Q", "*", "!",
+ /* 10 */ "S", null, ":", "-", null, ":", null, ":", "-", null,
+ /* 20 */ ":", null, ":", ";", "=", "-", "\"", null, ":", ";",
+ /* 30 */ "B", "\"", "|", null, ":", "-", "'", null, ":", "X",
+ /* 40 */ null, ":", null, ":", "-", null, ":", null, ":", "-",
+ /* 50 */ null, ":", null, ":", "-", null, ":", null, ":", "-",
+ /* 60 */ null, ":", null, ":", null, ":", "-", null, ":", null,
+ /* 70 */ ":", "-", null, ":", null, ":", "-", null, ":", null ];
+ var emoticons_states = [
+ /* 0 */ 12, 17, 22, 34, 43, 48, 53, 58, 65, 70,
+ /* 10 */ 75, 0, -15, 15, 0, -15, 0, -17, 20, 0,
+ /* 20 */ -17, 0, -14, -20, -14, 28, 63, 0, -14, -20,
+ /* 30 */ -3, 63, -18, 0, -12, 38, 41, 0, -12, -2,
+ /* 40 */ 0, -4, 0, -10, 46, 0, -10, 0, -19, 51,
+ /* 50 */ 0, -19, 0, -11, 56, 0, -11, 0, -13, 61,
+ /* 60 */ 0, -13, 0, -6, 0, 68, -7, 0, -7, 0,
+ /* 70 */ -16, 73, 0, -16, 0, -21, 78, 0, -21, 0 ];
+ var emoticons_icon_names = [
+ "face-angel",
+ "face-angry",
+ "face-cool",
+ "face-crying",
+ "face-devilish",
+ "face-embarrassed",
+ "face-kiss",
+ "face-laugh", /* not used */
+ "face-monkey", /* not used */
+ "face-plain",
+ "face-raspberry",
+ "face-sad",
+ "face-sick",
+ "face-smile",
+ "face-smile-big",
+ "face-smirk",
+ "face-surprise",
+ "face-tired",
+ "face-uncertain",
+ "face-wink",
+ "face-worried"
+ ];
+ var res = null, pos, state, start, uc;
+
+ start = text.length - 1;
+
+ if (start < 1)
+ return res;
+
+ pos = start;
+ while (pos >= 0) {
+ state = 0;
+ while (pos >= 0) {
+ uc = text[pos];
+ var relative = 0;
+ while (emoticons_chars[state + relative] != null) {
+ if (emoticons_chars[state + relative] == uc) {
+ break;
+ }
+ relative++;
+ }
+ state = emoticons_states[state + relative];
+ /* 0 .. not found, -n .. found n-th */
+ if (state <= 0)
+ break;
+ pos--;
+ }
+
+ /* Special case needed to recognize angel and devilish. */
+ if (pos > 0 && state == -14) {
+ uc = text[pos - 1];
+ if (uc == 'O') {
+ state = -1;
+ pos--;
+ } else if (uc == '>') {
+ state = -5;
+ pos--;
+ }
+ }
+
+ if (state < 0) {
+ if (pos > 0) {
+ uc = text[pos - 1];
+
+ if (uc != ' ') {
+ return res;
+ }
+ }
+
+ var obj = EvoEditor.lookupEmoticon(emoticons_icon_names[- state - 1], unicodeSmileys);
+
+ if (obj) {
+ obj.start = pos;
+ obj.end = start + 1;
+
+ if (!res)
+ res = [];
+
+ res[res.length] = obj;
+ }
+
+ pos--;
+ start = pos;
+ } else {
+ break;
+ }
+ }
+
+ return res;
+}
+
EvoEditor.AfterInputEvent = function(inputEvent, isWordDelim)
{
var isInsertParagraph = inputEvent.inputType == "insertParagraph";
@@ -2313,6 +2426,34 @@ EvoEditor.AfterInputEvent = function(inputEvent, isWordDelim)
var text = baseNode.nodeValue, covered = false;
+ var replaceMatchWithNode = function(opType, match, newNode, canEmit) {
+ EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, opType, baseNode.parentElement,
baseNode.parentElement,
+ EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+
+ try {
+ var offset = selection.baseOffset, updateSelection = selection.baseNode === baseNode,
newBaseNode;
+
+ baseNode.splitText(match.end);
+ newBaseNode = baseNode.nextSibling;
+ baseNode.splitText(match.start);
+
+ baseNode = baseNode.nextSibling;
+
+ baseNode.parentElement.insertBefore(newNode, baseNode);
+ baseNode.parentElement.removeChild(baseNode);
+
+ if (updateSelection && newBaseNode && offset - match.end >= 0)
+ selection.setPosition(newBaseNode, offset - match.end);
+ } finally {
+ EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, opType);
+
+ if (canEmit) {
+ EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_MAYBE);
+ EvoEditor.EmitContentChanged();
+ }
+ }
+ }
+
if (canLinks) {
var isEmail = text.search("@") >= 0, match;
@@ -2371,44 +2512,58 @@ EvoEditor.AfterInputEvent = function(inputEvent, isWordDelim)
}
if (url.length > 0) {
- EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "magicLink",
baseNode.parentElement, baseNode.parentElement,
- EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+ covered = true;
- try {
- var offset = selection.baseOffset, updateSelection =
selection.baseNode === baseNode, newBaseNode;
+ if (isEmail)
+ url = "mailto:" + url;
+ else if (url.startsWith("www."))
+ url = "https://" + url;
+
+ node = document.createElement("A");
+ node.href = url;
+
+ replaceMatchWithNode("magicLink", match, node, true);
+ }
+ }
+ }
- covered = true;
+ if (!covered && EvoEditor.MAGIC_SMILEYS) {
+ var matches;
- baseNode.splitText(match.end);
- newBaseNode = baseNode.nextSibling;
- baseNode.splitText(match.start);
+ // the replace call below replaces (0xA0) with regular space
+ matches = EvoEditor.findSmileys(text.replace(/ /g, " "), EvoEditor.UNICODE_SMILEYS);
+ if (matches) {
+ var sz = matches.length, node, tmpElement = null;
- baseNode = baseNode.nextSibling;
+ if (sz > 1)
+ EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_GROUP, "magicSmiley");
- if (isEmail)
- url = "mailto:" + url;
- else if (url.startsWith("www."))
- url = "http://" + url;
+ try {
+ // they are ordered from the end already
+ for (ii = 0; ii < sz; ii++) {
+ var match = matches[ii];
- node = document.createElement("A");
- node.href = url;
+ if (!match.imageUri || EvoEditor.UNICODE_SMILEYS || EvoEditor.mode !=
EvoEditor.MODE_HTML) {
+ node = document.createTextNode(match.text);
+ } else {
+ if (!tmpElement)
+ tmpElement = document.createElement("SPAN");
- baseNode.parentElement.insertBefore(node, baseNode);
- node.appendChild(baseNode);
+ tmpElement.innerHTML =
EvoEditor.createEmoticonHTML(match.text, match.imageUri, match.width, match.height);
+ node = tmpElement.firstChild;
+ }
- if (updateSelection && newBaseNode && offset - match.end >= 0)
- selection.setPosition(newBaseNode, offset - match.end);
- } finally {
- EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "magicLink");
+ replaceMatchWithNode("magicSmiley", match, node, sz == 1);
+ }
+ } finally {
+ if (sz > 1) {
+ EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_GROUP, "magicSmiley");
EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_MAYBE);
EvoEditor.EmitContentChanged();
}
}
}
}
-
- if (!covered && EvoEditor.MAGIC_SMILEYS) {
- }
}
EvoEditor.getParentElement = function(tagName, fromNode, canClimbUp)
@@ -3515,16 +3670,19 @@ EvoEditor.MoveSelectionToPoint = function(xx, yy, cancel_if_not_collapsed)
}
}
+EvoEditor.createEmoticonHTML = function(text, imageUri, width, height)
+{
+ if (imageUri && EvoEditor.mode == EvoEditor.MODE_HTML && !EvoEditor.UNICODE_SMILEYS)
+ return "<img src=\"" + imageUri + "\" alt=\"" +
+ text.replace(/\&/g, "&").replace(/\"/g, """).replace(/\'/g, "'") +
+ "\" width=\"" + width + "px\" height=\"" + height + "px\">";
+
+ return text;
+}
+
EvoEditor.InsertEmoticon = function(text, imageUri, width, height)
{
- if (imageUri) {
- EvoEditor.InsertHTML("InsertEmoticon",
- "<img src=\"" + imageUri + "\" alt=\"" +
- text.replace(/\&/g, "&").replace(/\"/g, """).replace(/\'/g,
"'") +
- "\" width=\"" + width + "px\" height=\"" + height + "px\">");
- } else {
- EvoEditor.InsertHTML("InsertEmoticon", text);
- }
+ EvoEditor.InsertHTML("InsertEmoticon", EvoEditor.createEmoticonHTML(text, imageUri, width, height));
}
EvoEditor.GetCurrentSignatureUid = function()
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 560a00d24c..2def8ee041 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
@@ -215,6 +215,60 @@ evo_editor_jsc_find_pattern (const gchar *text,
return object ? object : jsc_value_new_null (jsc_context);
}
+/* Returns 'null' or an object { text : string, imageUri : string, width : nnn, height : nnn }
+ where only the 'text' is required, describing an emoticon. */
+static JSCValue *
+evo_editor_jsc_lookup_emoticon (const gchar *iconName,
+ gboolean use_unicode_smileys,
+ JSCContext *jsc_context)
+{
+ JSCValue *object = NULL;
+
+ if (iconName && *iconName) {
+ const EEmoticon *emoticon;
+
+ emoticon = e_emoticon_chooser_lookup_emoticon (iconName);
+
+ if (emoticon) {
+ JSCValue *value;
+
+ object = jsc_value_new_object (jsc_context, NULL, NULL);
+
+ if (use_unicode_smileys) {
+ value = jsc_value_new_string (jsc_context, emoticon->unicode_character);
+ jsc_value_object_set_property (object, "text", value);
+ g_clear_object (&value);
+ } else {
+ gchar *image_uri;
+
+ value = jsc_value_new_string (jsc_context, emoticon->text_face);
+ jsc_value_object_set_property (object, "text", value);
+ g_clear_object (&value);
+
+ image_uri = e_emoticon_get_uri ((EEmoticon *) emoticon);
+
+ if (image_uri) {
+ value = jsc_value_new_string (jsc_context, image_uri);
+ jsc_value_object_set_property (object, "imageUri", value);
+ g_clear_object (&value);
+
+ value = jsc_value_new_number (jsc_context, 16);
+ jsc_value_object_set_property (object, "width", value);
+ g_clear_object (&value);
+
+ value = jsc_value_new_number (jsc_context, 16);
+ jsc_value_object_set_property (object, "height", value);
+ g_clear_object (&value);
+
+ g_free (image_uri);
+ }
+ }
+ }
+ }
+
+ return object ? object : jsc_value_new_null (jsc_context);
+}
+
static void
evo_editor_jsc_set_spell_check_languages (const gchar *langs,
GWeakRef *wkrf_extension)
@@ -312,6 +366,16 @@ window_object_cleared_cb (WebKitScriptWorld *world,
g_clear_object (&jsc_function);
+ /* EvoEditor.lookupEmoticon(iconName, useUnicodeSmileys) */
+ func_name = "lookupEmoticon";
+ jsc_function = jsc_value_new_function (jsc_context, func_name,
+ G_CALLBACK (evo_editor_jsc_lookup_emoticon), g_object_ref (jsc_context),
g_object_unref,
+ JSC_TYPE_VALUE, 2, G_TYPE_STRING, G_TYPE_BOOLEAN);
+
+ jsc_value_object_set_property (jsc_editor, func_name, jsc_function);
+
+ g_clear_object (&jsc_function);
+
/* EvoEditor.SetSpellCheckLanguages(langs) */
func_name = "SetSpellCheckLanguages";
jsc_function = jsc_value_new_function (jsc_context, func_name,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]