[geary/wip/728002-webkit2: 93/105] Initial pass at implementing Format=Flowed formatting in JS for WK2.
- From: Michael Gratton <mjog src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/wip/728002-webkit2: 93/105] Initial pass at implementing Format=Flowed formatting in JS for WK2.
- Date: Sun, 1 Jan 2017 23:57:40 +0000 (UTC)
commit 56a427672d4a377ac85ec316bdd38dc920931a5a
Author: Michael James Gratton <mike vee net>
Date: Mon Dec 5 10:07:23 2016 +1100
Initial pass at implementing Format=Flowed formatting in JS for WK2.
May not actually be working, needs testing, may eat your replies, etc.
src/client/web-process/util-webkit.vala | 126 ---------------------------
ui/composer-web-view.js | 144 ++++++++++++++++++++++++++++++-
2 files changed, 143 insertions(+), 127 deletions(-)
---
diff --git a/src/client/web-process/util-webkit.vala b/src/client/web-process/util-webkit.vala
index 06ce9af..8d8fba2 100644
--- a/src/client/web-process/util-webkit.vala
+++ b/src/client/web-process/util-webkit.vala
@@ -7,10 +7,6 @@
// Regex to determine if a URL has a known protocol.
public const string PROTOCOL_REGEX =
"^(aim|apt|bitcoin|cvs|ed2k|ftp|file|finger|git|gtalk|http|https|irc|ircs|irc6|lastfm|ldap|ldaps|magnet|news|nntp|rsync|sftp|skype|smb|sms|svn|telnet|tftp|ssh|webcal|xmpp):";
-// Private use unicode characters are used for quote tokens
-public const string QUOTE_START = "";
-public const string QUOTE_END = "";
-
namespace Util.DOM {
public WebKit.DOM.HTMLElement? select(WebKit.DOM.Node node, string selector) {
try {
@@ -298,126 +294,4 @@ namespace Util.DOM {
return outtext;
}
- /**
- * Convert a HTML DOM tree to RFC 3676 format=flowed text.
- *
- * This will modify/reset the DOM.
- */
- public string html_to_flowed_text(WebKit.DOM.HTMLElement el) {
- string saved_doc = el.get_inner_html();
- WebKit.DOM.NodeList blockquotes;
- try {
- blockquotes = el.query_selector_all("blockquote");
- } catch (Error error) {
- debug("Error selecting blockquotes: %s", error.message);
- return "";
- }
-
- int nbq = (int) blockquotes.length;
- string[] bqtexts = new string[nbq];
-
- // Get text of blockquotes and pull them out of DOM. They are replaced with tokens deliminated
- // with the characters QUOTE_START and QUOTE_END (from a unicode private use block). We need to
- // get the text while they're still in the DOM to get newlines at appropriate places. We go
- // through the list of blockquotes from the end so that we get the innermost ones first.
- for (int i = nbq - 1; i >= 0; i--) {
- WebKit.DOM.HTMLElement bq = (WebKit.DOM.HTMLElement) blockquotes.item(i);
- bqtexts[i] = bq.get_inner_text();
- if (bqtexts[i].length > 0 && bqtexts[i].substring(-1, 1) == "\n")
- bqtexts[i] = bqtexts[i].slice(0, -1);
- else
- debug("Did not find expected newline at end of quote.");
-
- try {
- bq.set_inner_text(@"$QUOTE_START$i$QUOTE_END");
- } catch (Error error) {
- debug("Error manipulating DOM: %s", error.message);
- }
- }
-
- // Reassemble plain text out of parts, replace non-breaking space with regular space
- string doctext = resolve_nesting(el.get_inner_text(), bqtexts).replace("\xc2\xa0", " ");
-
- // Reassemble DOM
- try {
- el.set_inner_html(saved_doc);
- } catch (Error error) {
- debug("Error resetting DOM: %s", error.message);
- }
-
- // Wrap, space stuff, quote
- string[] lines = doctext.split("\n");
- GLib.StringBuilder flowed = new GLib.StringBuilder.sized(doctext.length);
- foreach (string line in lines) {
- // Strip trailing whitespace, so it doesn't look like a flowed line. But the
- // signature separator "-- " is special, so leave that alone.
- if (line != "-- ")
- line = line.chomp();
- int quote_level = 0;
- while (line[quote_level] == Geary.RFC822.Utils.QUOTE_MARKER)
- quote_level += 1;
- line = line[quote_level:line.length];
- string prefix = quote_level > 0 ? string.nfill(quote_level, '>') + " " : "";
- int max_len = 72 - prefix.length;
-
- do {
- int start_ind = 0;
- if (quote_level == 0 &&
- (line.has_prefix(">") || line.has_prefix("From"))) {
- line = " " + line;
- start_ind = 1;
- }
-
- int cut_ind = line.length;
- if (cut_ind > max_len) {
- string beg = line[0:max_len];
- cut_ind = beg.last_index_of(" ", start_ind) + 1;
- if (cut_ind == 0) {
- cut_ind = line.index_of(" ", start_ind) + 1;
- if (cut_ind == 0)
- cut_ind = line.length;
- if (cut_ind > 998 - prefix.length)
- cut_ind = 998 - prefix.length;
- }
- }
- flowed.append(prefix + line[0:cut_ind] + "\n");
- line = line[cut_ind:line.length];
- } while (line.length > 0);
- }
-
- return flowed.str;
- }
-
- public string quote_lines(string text) {
- string[] lines = text.split("\n");
- for (int i=0; i<lines.length; i++)
- lines[i] = @"$(Geary.RFC822.Utils.QUOTE_MARKER)" + lines[i];
- return string.joinv("\n", lines);
- }
-
- public string resolve_nesting(string text, string[] values) {
- try {
- GLib.Regex tokenregex = new GLib.Regex(@"(.?)$QUOTE_START([0-9]*)$QUOTE_END(?=(.?))");
- return tokenregex.replace_eval(text, -1, 0, 0, (info, res) => {
- int key = int.parse(info.fetch(2));
- string prev_char = info.fetch(1), next_char = info.fetch(3), insert_next = "";
- // Make sure there's a newline before and after the quote.
- if (prev_char != "" && prev_char != "\n")
- prev_char = prev_char + "\n";
- if (next_char != "" && next_char != "\n")
- insert_next = "\n";
- if (key >= 0 && key < values.length) {
- res.append(prev_char + quote_lines(resolve_nesting(values[key], values)) + insert_next);
- } else {
- debug("Regex error in denesting blockquotes: Invalid key");
- res.append("");
- }
- return false;
- });
- } catch (Error error) {
- debug("Regex error in denesting blockquotes: %s", error.message);
- return "";
- }
- }
-
}
diff --git a/ui/composer-web-view.js b/ui/composer-web-view.js
index 0089132..8fa0dee 100644
--- a/ui/composer-web-view.js
+++ b/ui/composer-web-view.js
@@ -13,6 +13,9 @@ var ComposerPageState = function() {
this.init.apply(this, arguments);
};
ComposerPageState.BODY_ID = "message-body";
+ComposerPageState.QUOTE_START = "";
+ComposerPageState.QUOTE_END = "";
+ComposerPageState.QUOTE_MARKER = "\x7f";
ComposerPageState.prototype = {
__proto__: PageState.prototype,
@@ -63,7 +66,9 @@ ComposerPageState.prototype = {
return document.getElementById(ComposerPageState.BODY_ID).innerHTML;
},
getText: function() {
- return document.getElementById(ComposerPageState.BODY_ID).innerText;
+ return ComposerPageState.htmlToFlowedText(
+ document.getElementById(ComposerPageState.BODY_ID)
+ );
},
setRichText: function(enabled) {
if (enabled) {
@@ -87,6 +92,143 @@ ComposerPageState.prototype = {
}
};
+/**
+ * Convert a HTML DOM tree to RFC 3676 format=flowed text.
+ *
+ * This will modify/reset the DOM.
+ */
+ComposerPageState.htmlToFlowedText = function(root) {
+ var savedDoc = root.innerHTML;
+ var blockquotes = root.querySelectorAll("blockquote");
+ var nbq = blockquotes.length;
+ var bqtexts = new Array(nbq);
+
+ // Get text of blockquotes and pull them out of DOM. They are
+ // replaced with tokens deliminated with the characters
+ // QUOTE_START and QUOTE_END (from a unicode private use block).
+ // We need to get the text while they're still in the DOM to get
+ // newlines at appropriate places. We go through the list of
+ // blockquotes from the end so that we get the innermost ones
+ // first.
+ for (let i = nbq - 1; i >= 0; i--) {
+ let bq = blockquotes.item(i);
+ let text = bq.innerText;
+ console.log("Line: " + text);
+ if (text.substr(-1, 1) == "\n") {
+ text = text.slice(0, -1);
+ console.log(" found expected newline at end of quote!");
+ } else {
+ console.log(
+ " no newline at end of quote: " +
+ text.length > 0
+ ? "0x" + text.codePointAt(text.length - 1).toString(16)
+ : "empty line"
+ );
+ }
+ bqtexts[i] = text;
+
+ bq.innerText = (
+ ComposerPageState.QUOTE_START
+ + i.toString()
+ + ComposerPageState.QUOTE_END
+ );
+ }
+
+ // Reassemble plain text out of parts, replace non-breaking
+ // space with regular space
+ var doctext = ComposerPageState.resolveNesting(
+ root.innerText, bqtexts
+ ).replace("\xc2\xa0", " ");
+
+ // Reassemble DOM
+ root.innerHTML = savedDoc;
+
+ // Wrap, space stuff, quote
+ var lines = doctext.split("\n");
+ flowed = [];
+ for (let line of lines) {
+ // Strip trailing whitespace, so it doesn't look like a flowed
+ // line. But the signature separator "-- " is special, so
+ // leave that alone.
+ if (line != "-- ") {
+ line = line.trimRight();
+ }
+ let quoteLevel = 0;
+ while (line[quoteLevel] == ComposerPageState.QUOTE_MARKER) {
+ quoteLevel += 1;
+ }
+ line = line.substr(quoteLevel, line.length);
+ let prefix = quoteLevel > 0 ? '>'.repeat(quoteLevel) + " " : "";
+ let maxLen = 72 - prefix.length;
+
+ do {
+ let startInd = 0;
+ if (quoteLevel == 0 &&
+ (line.startsWith(">") || line.startsWith("From"))) {
+ line = " " + line;
+ startInd = 1;
+ }
+
+ let cutInd = line.length;
+ if (cutInd > maxLen) {
+ let beg = line.substr(0, maxLen);
+ cutInd = beg.lastIndexOf(" ", startInd) + 1;
+ if (cutInd == 0) {
+ cutInd = line.indexOf(" ", startInd) + 1;
+ if (cutInd == 0) {
+ cutInd = line.length;
+ }
+ if (cutInd > 998 - prefix.length) {
+ cutInd = 998 - prefix.length;
+ }
+ }
+ }
+ flowed.push(prefix + line.substr(0, cutInd) + "\n");
+ line = line.substr(cutInd, line.length);
+ } while (line.length > 0);
+ }
+
+ return flowed.join("");
+};
+
+ComposerPageState.resolveNesting = function(text, values) {
+ let tokenregex = new RegExp(
+ "(.?)" +
+ ComposerPageState.QUOTE_START +
+ "([0-9]*)" +
+ ComposerPageState.QUOTE_END +
+ "(?=(.?))"
+ );
+ return text.replace(tokenregex, function(match, p1, p2, p3, offset, str) {
+ let key = new Number(p2);
+ let prevChar = p1;
+ let nextChar = p3;
+ let insertNext = "";
+ // Make sure there's a newline before and after the quote.
+ if (prevChar != "" && prevChar != "\n")
+ prevChar = prevChar + "\n";
+ if (nextChar != "" && nextChar != "\n")
+ insertNext = "\n";
+
+ let value = "";
+ if (key >= 0 && key < values.length) {
+ let nested = ComposerPageState.resolveNesting(values[key], values);
+ value = prevChar + ComposerPageState.quoteLines(nested) + insertNext;
+ } else {
+ console.log("Regex error in denesting blockquotes: Invalid key");
+ }
+ return value;
+ });
+};
+
+ComposerPageState.quoteLines = function(text) {
+ let lines = text.split("\n");
+ for (let i = 0; i < lines.length; i++)
+ lines[i] = ComposerPageState.QUOTE_MARKER + lines[i];
+ return lines.join("\n");
+};
+
+
var geary = new ComposerPageState();
window.onload = function() {
geary.loaded();
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]