[geary] Moved DOM function into Util.DOM namespace
- From: Michael Gratton <mjog src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary] Moved DOM function into Util.DOM namespace
- Date: Sat, 21 May 2016 02:49:49 +0000 (UTC)
commit 591dd2620c70012b64837e6e33074d5f9f479df8
Author: Niels De Graef <nielsdegraef gmail com>
Date: Sun May 8 22:41:51 2016 +0200
Moved DOM function into Util.DOM namespace
Signed-off-by: Niels De Graef <nielsdegraef gmail com>
Bug 766100
src/client/accounts/add-edit-page.vala | 2 +-
src/client/composer/composer-widget.vala | 12 +-
.../conversation-viewer/conversation-viewer.vala | 77 ++-
.../conversation-viewer/conversation-web-view.vala | 4 +-
src/client/util/util-webkit.vala | 669 ++++++++++----------
5 files changed, 390 insertions(+), 374 deletions(-)
---
diff --git a/src/client/accounts/add-edit-page.vala b/src/client/accounts/add-edit-page.vala
index e285a48..49f6b2b 100644
--- a/src/client/accounts/add-edit-page.vala
+++ b/src/client/accounts/add-edit-page.vala
@@ -590,7 +590,7 @@ public class AddEditPage : Gtk.Box {
private void on_signature_stack_changed() {
if (signature_stack.visible_child_name == "preview_window")
- preview_webview.load_html_string(smart_escape(email_signature, true), "");
+ preview_webview.load_html_string(Util.DOM.smart_escape(email_signature, true), "");
}
private uint16 get_default_smtp_port() {
diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala
index db571c1..18af0e5 100644
--- a/src/client/composer/composer-widget.vala
+++ b/src/client/composer/composer-widget.vala
@@ -804,7 +804,7 @@ public class ComposerWidget : Gtk.EventBox {
// Ensure the editor is in correct mode re HTML
on_compose_as_html();
- bind_event(editor,"a", "click", (Callback) on_link_clicked, this);
+ Util.DOM.bind_event(editor,"a", "click", (Callback) on_link_clicked, this);
update_actions();
on_show_extended();
@@ -1051,7 +1051,7 @@ public class ComposerWidget : Gtk.EventBox {
set_cursor();
return;
}
- signature = smart_escape(signature, false);
+ signature = Util.DOM.smart_escape(signature, false);
} catch (Error error) {
debug("Error reading signature file %s: %s", signature_file.get_path(), error.message);
set_cursor();
@@ -1063,7 +1063,7 @@ public class ComposerWidget : Gtk.EventBox {
set_cursor();
return;
}
- signature = smart_escape(signature, true);
+ signature = Util.DOM.smart_escape(signature, true);
}
if (body_html == null)
@@ -1287,7 +1287,7 @@ public class ComposerWidget : Gtk.EventBox {
container.vanish();
is_closing = true;
- linkify_document(editor.get_dom_document());
+ Util.DOM.linkify_document(editor.get_dom_document());
// Perform send.
try {
@@ -2000,7 +2000,7 @@ public class ComposerWidget : Gtk.EventBox {
dialog.destroy();
// Re-bind to anchor links. This must be done every time link have changed.
- bind_event(editor,"a", "click", (Callback) on_link_clicked, this);
+ Util.DOM.bind_event(editor,"a", "click", (Callback) on_link_clicked, this);
}
private string get_html() {
@@ -2009,7 +2009,7 @@ public class ComposerWidget : Gtk.EventBox {
}
private string get_text() {
- return html_to_flowed_text((WebKit.DOM.HTMLElement) editor.get_dom_document()
+ return Util.DOM.html_to_flowed_text((WebKit.DOM.HTMLElement) editor.get_dom_document()
.get_element_by_id(BODY_ID));
}
diff --git a/src/client/conversation-viewer/conversation-viewer.vala
b/src/client/conversation-viewer/conversation-viewer.vala
index 3c1d9f0..2d03754 100644
--- a/src/client/conversation-viewer/conversation-viewer.vala
+++ b/src/client/conversation-viewer/conversation-viewer.vala
@@ -294,7 +294,7 @@ public class ConversationViewer : Gtk.Box {
ancestor = ancestor_node.get_parent_element();
// If the selection is part of a plain text message, we have to stick it in
// an appropriately styled div, so that new lines are preserved.
- if (is_descendant_of(ancestor, ".plaintext")) {
+ if (Util.DOM.is_descendant_of(ancestor, ".plaintext")) {
dummy.get_class_list().add("plaintext");
dummy.set_attribute("style", "white-space: pre-wrap;");
include_dummy = true;
@@ -750,23 +750,40 @@ public class ConversationViewer : Gtk.Box {
});
// Attach to the click events for hiding/showing quotes, opening the menu, and so forth.
- bind_event(web_view, ".email", "contextmenu", (Callback) on_context_menu, this);
- bind_event(web_view, ".quote_container > .hider", "click", (Callback) on_hide_quote_clicked);
- bind_event(web_view, ".quote_container > .shower", "click", (Callback) on_show_quote_clicked);
- bind_event(web_view, ".email_container .menu", "click", (Callback) on_menu_clicked, this);
- bind_event(web_view, ".email_container .starred", "click", (Callback) on_unstar_clicked, this);
- bind_event(web_view, ".email_container .unstarred", "click", (Callback) on_star_clicked, this);
- bind_event(web_view, ".email_container .draft_edit .button", "click", (Callback) on_draft_edit_menu,
this);
- bind_event(web_view, ".header .field .value", "click", (Callback) on_value_clicked, this);
- bind_event(web_view, ".email:not(:only-of-type) .header_container, .email .email
.header_container","click", (Callback) on_body_toggle_clicked, this);
- bind_event(web_view, ".email .compressed_note", "click", (Callback) on_body_toggle_clicked, this);
- bind_event(web_view, ".attachment_container .attachment", "click", (Callback) on_attachment_clicked,
this);
- bind_event(web_view, ".attachment_container .attachment", "contextmenu", (Callback)
on_attachment_menu, this);
- bind_event(web_view, "." + DATA_IMAGE_CLASS, "contextmenu", (Callback) on_data_image_menu_handler,
this);
- bind_event(web_view, ".remote_images .show_images", "click", (Callback) on_show_images, this);
- bind_event(web_view, ".remote_images .show_from", "click", (Callback) on_show_images_from, this);
- bind_event(web_view, ".remote_images .close_show_images", "click", (Callback) on_close_show_images,
this);
- bind_event(web_view, ".body a", "click", (Callback) on_link_clicked, this);
+ Util.DOM.bind_event(web_view, ".email", "contextmenu",
+ (Callback) on_context_menu, this);
+ Util.DOM.bind_event(web_view, ".quote_container > .hider", "click",
+ (Callback) on_hide_quote_clicked);
+ Util.DOM.bind_event(web_view, ".quote_container > .shower", "click",
+ (Callback) on_show_quote_clicked);
+ Util.DOM.bind_event(web_view, ".email_container .menu", "click",
+ (Callback) on_menu_clicked, this);
+ Util.DOM.bind_event(web_view, ".email_container .starred", "click",
+ (Callback) on_unstar_clicked, this);
+ Util.DOM.bind_event(web_view, ".email_container .unstarred", "click",
+ (Callback) on_star_clicked, this);
+ Util.DOM.bind_event(web_view, ".email_container .draft_edit .button", "click",
+ (Callback) on_draft_edit_menu, this);
+ Util.DOM.bind_event(web_view, ".header .field .value", "click",
+ (Callback) on_value_clicked, this);
+ Util.DOM.bind_event(web_view, ".email:not(:only-of-type) .header_container, .email .email
.header_container","click",
+ (Callback) on_body_toggle_clicked, this);
+ Util.DOM.bind_event(web_view, ".email .compressed_note", "click",
+ (Callback) on_body_toggle_clicked, this);
+ Util.DOM.bind_event(web_view, ".attachment_container .attachment", "click",
+ (Callback) on_attachment_clicked, this);
+ Util.DOM.bind_event(web_view, ".attachment_container .attachment", "contextmenu",
+ (Callback) on_attachment_menu, this);
+ Util.DOM.bind_event(web_view, "." + DATA_IMAGE_CLASS, "contextmenu",
+ (Callback) on_data_image_menu_handler, this);
+ Util.DOM.bind_event(web_view, ".remote_images .show_images", "click",
+ (Callback) on_show_images, this);
+ Util.DOM.bind_event(web_view, ".remote_images .show_from", "click",
+ (Callback) on_show_images_from, this);
+ Util.DOM.bind_event(web_view, ".remote_images .close_show_images", "click",
+ (Callback) on_close_show_images, this);
+ Util.DOM.bind_event(web_view, ".body a", "click",
+ (Callback) on_link_clicked, this);
// Update the search results
if (conversation_find_bar.visible)
@@ -897,7 +914,7 @@ public class ConversationViewer : Gtk.Box {
for (int i = 0; i < style_sheets.length; i++) {
WebKit.DOM.StyleSheet style_sheet = style_sheets.item(i);
WebKit.DOM.Element style_element = (WebKit.DOM.Element) style_sheet.owner_node;
- if (closest_ancestor(style_element, ".email") == div_message)
+ if (Util.DOM.closest_ancestor(style_element, ".email") == div_message)
style_element.set_text_content(scope_style_sheet(style_sheet, div_id));
}
} catch (Error error) {
@@ -1010,7 +1027,7 @@ public class ConversationViewer : Gtk.Box {
return "<img alt=\"%s\" class=\"%s %s\" src=\"%s\" replaced-id=\"%s\" %s />".printf(
Geary.HTML.escape_markup(filename),
DATA_IMAGE_CLASS, REPLACED_IMAGE_CLASS,
- assemble_data_uri(mime_type, rotated_image),
+ Util.DOM.assemble_data_uri(mime_type, rotated_image),
Geary.HTML.escape_markup(replaced_image.id),
escaped_content_id != null ? @"cid=\"$escaped_content_id\"" : "");
}
@@ -1185,7 +1202,7 @@ public class ConversationViewer : Gtk.Box {
if (element.webkit_matches_selector(".email")) {
email_element = element;
} else {
- email_element = closest_ancestor(element, ".email");
+ email_element = Util.DOM.closest_ancestor(element, ".email");
}
} catch (Error error) {
debug("Failed to find div.email from element: %s", error.message);
@@ -1385,7 +1402,7 @@ public class ConversationViewer : Gtk.Box {
private bool is_hidden_email(WebKit.DOM.Element element) {
try {
- WebKit.DOM.HTMLElement? email_element = closest_ancestor(element, ".email");
+ WebKit.DOM.HTMLElement? email_element = Util.DOM.closest_ancestor(element, ".email");
if (email_element == null)
return false;
@@ -1407,7 +1424,7 @@ public class ConversationViewer : Gtk.Box {
if (web_view.get_dom_document().get_body().get_class_list().contains("nohide"))
return;
- WebKit.DOM.HTMLElement? email_element = closest_ancestor(element, ".email");
+ WebKit.DOM.HTMLElement? email_element = Util.DOM.closest_ancestor(element, ".email");
if (email_element == null)
return;
@@ -1429,7 +1446,7 @@ public class ConversationViewer : Gtk.Box {
private static void on_show_images(WebKit.DOM.Element element, WebKit.DOM.Event event,
ConversationViewer conversation_viewer) {
- WebKit.DOM.HTMLElement? email_element = closest_ancestor(element, ".email");
+ WebKit.DOM.HTMLElement? email_element = Util.DOM.closest_ancestor(element, ".email");
if (email_element != null)
conversation_viewer.show_images_email(email_element, true);
}
@@ -1519,7 +1536,7 @@ public class ConversationViewer : Gtk.Box {
private static void on_close_show_images(WebKit.DOM.Element element, WebKit.DOM.Event event,
ConversationViewer conversation_viewer) {
- WebKit.DOM.HTMLElement? remote_images = closest_ancestor(element, ".remote_images");
+ WebKit.DOM.HTMLElement? remote_images = Util.DOM.closest_ancestor(element, ".remote_images");
if (remote_images != null) {
try {
remote_images.get_class_list().remove("show");
@@ -1567,7 +1584,7 @@ public class ConversationViewer : Gtk.Box {
} catch (Error error) {
warning("Error showing link warning dialog: %s", error.message);
}
- bind_event(web_view, ".link_warning .close_link_warning, .link_warning a", "click",
+ Util.DOM.bind_event(web_view, ".link_warning .close_link_warning, .link_warning a", "click",
(Callback) on_close_link_warning, this);
return true;
}
@@ -1655,7 +1672,7 @@ public class ConversationViewer : Gtk.Box {
private static void on_close_link_warning(WebKit.DOM.Element element, WebKit.DOM.Event event,
ConversationViewer conversation_viewer) {
try {
- WebKit.DOM.Element warning_div = closest_ancestor(element, ".link_warning");
+ WebKit.DOM.Element warning_div = Util.DOM.closest_ancestor(element, ".link_warning");
WebKit.DOM.Element link = (WebKit.DOM.Element) warning_div.get_next_sibling();
link.remove_attribute("warning");
warning_div.parent_node.remove_child(warning_div);
@@ -1960,7 +1977,7 @@ public class ConversationViewer : Gtk.Box {
WebKit.DOM.Node parent = blockquote_node.get_parent_node();
// Make sure this is a top level blockquote.
- if (node_is_child_of(blockquote_node, "BLOCKQUOTE")) {
+ if (Util.DOM.node_is_child_of(blockquote_node, "BLOCKQUOTE")) {
continue;
}
@@ -2019,7 +2036,7 @@ public class ConversationViewer : Gtk.Box {
// Replace the SRC to a data URI, the class to a known label for the popup menu,
// and the ALT to its filename, if supplied
img.remove_attribute("src"); // Work around a WebKitGTK+ crash. Bug 764152
- img.set_attribute("src", assemble_data_uri(mimetype, image_content));
+ img.set_attribute("src", Util.DOM.assemble_data_uri(mimetype, image_content));
img.set_attribute("class", DATA_IMAGE_CLASS);
if (!Geary.String.is_empty(filename))
img.set_attribute("alt", filename);
@@ -2069,7 +2086,7 @@ public class ConversationViewer : Gtk.Box {
WebKit.DOM.HTMLElement div = div_list.item(i) as WebKit.DOM.HTMLElement;
string inner_html = div.get_inner_html();
if ((sig_regex.match(inner_html) || alternate_sig_regex.match(inner_html)) &&
- !node_is_child_of(div, "BLOCKQUOTE")) {
+ !Util.DOM.node_is_child_of(div, "BLOCKQUOTE")) {
break;
}
}
diff --git a/src/client/conversation-viewer/conversation-web-view.vala
b/src/client/conversation-viewer/conversation-web-view.vala
index 50ed18c..57dcbd0 100644
--- a/src/client/conversation-viewer/conversation-web-view.vala
+++ b/src/client/conversation-viewer/conversation-web-view.vala
@@ -247,7 +247,7 @@ public class ConversationWebView : StylishWebView {
// Then set the source to a data url.
WebKit.DOM.HTMLImageElement img = Util.DOM.select(get_dom_document(), selector)
as WebKit.DOM.HTMLImageElement;
- img.set_attribute("src", assemble_data_uri("image/png", buffer));
+ img.set_attribute("src", Util.DOM.assemble_data_uri("image/png", buffer));
} catch (Error error) {
warning("Failed to load icon '%s': %s", icon_name, error.message);
}
@@ -289,7 +289,7 @@ public class ConversationWebView : StylishWebView {
Geary.Memory.Buffer buffer = new Geary.Memory.ByteBuffer.take((owned) content,
content_length);
- img.set_attribute("src", assemble_data_uri("image/png", buffer));
+ img.set_attribute("src", Util.DOM.assemble_data_uri("image/png", buffer));
} catch (Error error) {
warning("Failed to load image '%s': %s", filename, error.message);
}
diff --git a/src/client/util/util-webkit.vala b/src/client/util/util-webkit.vala
index 98b06ac..b542449 100644
--- a/src/client/util/util-webkit.vala
+++ b/src/client/util/util-webkit.vala
@@ -15,7 +15,6 @@ public const string PROTOCOL_REGEX = "^(aim|apt|bitcoin|cvs|ed2k|ftp|file|finger
public const string QUOTE_START = "";
public const string QUOTE_END = "";
-// TODO Move these other functions and variables into this namespace.
namespace Util.DOM {
public WebKit.DOM.HTMLElement? select(WebKit.DOM.Node node, string selector) {
try {
@@ -46,7 +45,7 @@ namespace Util.DOM {
class_list.remove(clas);
}
}
-
+
// Returns the text contained in the DOM document, after ignoring tags of type "exclude"
// and padding newlines where appropriate. Used to scan for attachment keywords.
public string get_text_representation(WebKit.DOM.Document doc, string exclude) {
@@ -54,14 +53,14 @@ namespace Util.DOM {
if (copy == null) {
return "";
}
-
+
// Keep deleting the next excluded element until there are none left
while (true) {
WebKit.DOM.HTMLElement? current = Util.DOM.select(copy, exclude);
if (current == null) {
break;
}
-
+
WebKit.DOM.Node parent = current.get_parent_node();
try {
parent.remove_child(current);
@@ -70,7 +69,7 @@ namespace Util.DOM {
break;
}
}
-
+
WebKit.DOM.NodeList node_list;
try {
node_list = copy.query_selector_all("br");
@@ -78,7 +77,7 @@ namespace Util.DOM {
debug("Error finding <br>s: %s", error.message);
return copy.get_inner_text();
}
-
+
// Replace <br> tags with newlines
for (int i = 0; i < node_list.length; ++i) {
WebKit.DOM.Node br = node_list.item(i);
@@ -89,14 +88,14 @@ namespace Util.DOM {
debug("Error replacing <br>: %s", error.message);
}
}
-
+
try {
node_list = copy.query_selector_all("div");
} catch (Error error) {
debug("Error finding <div>s: %s", error.message);
return copy.get_inner_text();
}
-
+
// Pad each <div> with newlines
for (int i = 0; i < node_list.length; ++i) {
WebKit.DOM.Node div = node_list.item(i);
@@ -109,378 +108,378 @@ namespace Util.DOM {
}
return copy.get_inner_text();
}
-}
-public void bind_event(WebKit.WebView view, string selector, string event, Callback callback,
- Object? extra = null) {
- try {
- WebKit.DOM.NodeList node_list = view.get_dom_document().query_selector_all(selector);
- for (int i = 0; i < node_list.length; ++i) {
- WebKit.DOM.EventTarget node = node_list.item(i) as WebKit.DOM.EventTarget;
- node.remove_event_listener(event, callback, false);
- node.add_event_listener(event, callback, false, extra);
+ public void bind_event(WebKit.WebView view, string selector, string event, Callback callback,
+ Object? extra = null) {
+ try {
+ WebKit.DOM.NodeList node_list = view.get_dom_document().query_selector_all(selector);
+ for (int i = 0; i < node_list.length; ++i) {
+ WebKit.DOM.EventTarget node = node_list.item(i) as WebKit.DOM.EventTarget;
+ node.remove_event_listener(event, callback, false);
+ node.add_event_listener(event, callback, false, extra);
+ }
+ } catch (Error error) {
+ warning("Error setting up click handlers: %s", error.message);
}
- } catch (Error error) {
- warning("Error setting up click handlers: %s", error.message);
}
-}
-
-// Linkifies plain text links in an HTML document.
-public void linkify_document(WebKit.DOM.Document document) {
- linkify_recurse(document, document.get_body());
-}
-// Validates a URL.
-// Ensures the URL begins with a valid protocol specifier. (If not, we don't
-// want to linkify it.)
-// Note that the output of this will place '\01' chars before and after a link,
-// which we use to split the string in linkify()
-private bool pre_split_urls(MatchInfo match_info, StringBuilder result) {
- try {
- string? url = match_info.fetch(0);
- Regex r = new Regex(PROTOCOL_REGEX, RegexCompileFlags.CASELESS);
- result.append(r.match(url) ? "\01%s\01".printf(url) : url);
- } catch (Error e) {
- debug("URL parsing error: %s\n", e.message);
+ // Linkifies plain text links in an HTML document.
+ public void linkify_document(WebKit.DOM.Document document) {
+ linkify_recurse(document, document.get_body());
}
- return false; // False to continue processing.
-}
-// Linkifies "plain text" links in the HTML doc. If you want to do this
-// for the entire document, use the get_dom_document().get_body() for the
-// node param and leave _in_link as false.
-private void linkify_recurse(WebKit.DOM.Document document, WebKit.DOM.Node node,
- bool _in_link = false) {
-
- bool in_link = _in_link;
- if (node is WebKit.DOM.HTMLAnchorElement)
- in_link = true;
-
- string input = node.get_node_value();
- if (!in_link && !Geary.String.is_empty(input)) {
+ // Validates a URL.
+ // Ensures the URL begins with a valid protocol specifier. (If not, we don't
+ // want to linkify it.)
+ // Note that the output of this will place '\01' chars before and after a link,
+ // which we use to split the string in linkify()
+ private bool pre_split_urls(MatchInfo match_info, StringBuilder result) {
try {
- Regex r = new Regex(URL_REGEX, RegexCompileFlags.CASELESS);
- string output = r.replace_eval(input, -1, 0, 0, pre_split_urls);
- if (input != output) {
- // We got one! Now split the text and swap out the node.
- Regex tester = new Regex(PROTOCOL_REGEX, RegexCompileFlags.CASELESS);
- string[] pieces = output.split("\01");
- Gee.ArrayList<WebKit.DOM.Node> new_nodes = new Gee.ArrayList<WebKit.DOM.Node>();
-
- for(int i = 0; i < pieces.length; i++) {
- //WebKit.DOM.Node new_node;
- if (tester.match(pieces[i])) {
- // Link part.
- WebKit.DOM.HTMLAnchorElement anchor = document.create_element("a")
- as WebKit.DOM.HTMLAnchorElement;
- anchor.href = pieces[i];
- anchor.set_inner_text(pieces[i]);
- new_nodes.add(anchor);
- } else {
- // Text part.
- WebKit.DOM.Node new_node = node.clone_node(false);
- new_node.set_node_value(pieces[i]);
- new_nodes.add(new_node);
+ string? url = match_info.fetch(0);
+ Regex r = new Regex(PROTOCOL_REGEX, RegexCompileFlags.CASELESS);
+ result.append(r.match(url) ? "\01%s\01".printf(url) : url);
+ } catch (Error e) {
+ debug("URL parsing error: %s\n", e.message);
+ }
+ return false; // False to continue processing.
+ }
+
+ // Linkifies "plain text" links in the HTML doc. If you want to do this
+ // for the entire document, use the get_dom_document().get_body() for the
+ // node param and leave _in_link as false.
+ private void linkify_recurse(WebKit.DOM.Document document, WebKit.DOM.Node node,
+ bool _in_link = false) {
+
+ bool in_link = _in_link;
+ if (node is WebKit.DOM.HTMLAnchorElement)
+ in_link = true;
+
+ string input = node.get_node_value();
+ if (!in_link && !Geary.String.is_empty(input)) {
+ try {
+ Regex r = new Regex(URL_REGEX, RegexCompileFlags.CASELESS);
+ string output = r.replace_eval(input, -1, 0, 0, pre_split_urls);
+ if (input != output) {
+ // We got one! Now split the text and swap out the node.
+ Regex tester = new Regex(PROTOCOL_REGEX, RegexCompileFlags.CASELESS);
+ string[] pieces = output.split("\01");
+ Gee.ArrayList<WebKit.DOM.Node> new_nodes = new Gee.ArrayList<WebKit.DOM.Node>();
+
+ for(int i = 0; i < pieces.length; i++) {
+ //WebKit.DOM.Node new_node;
+ if (tester.match(pieces[i])) {
+ // Link part.
+ WebKit.DOM.HTMLAnchorElement anchor = document.create_element("a")
+ as WebKit.DOM.HTMLAnchorElement;
+ anchor.href = pieces[i];
+ anchor.set_inner_text(pieces[i]);
+ new_nodes.add(anchor);
+ } else {
+ // Text part.
+ WebKit.DOM.Node new_node = node.clone_node(false);
+ new_node.set_node_value(pieces[i]);
+ new_nodes.add(new_node);
+ }
}
+
+ // Add our new nodes.
+ WebKit.DOM.Node? sibling = node.get_next_sibling();
+ for (int i = 0; i < new_nodes.size; i++) {
+ WebKit.DOM.Node new_node = new_nodes.get(i);
+ if (sibling == null)
+ node.get_parent_node().append_child(new_node);
+ else
+ node.get_parent_node().insert_before(new_node, sibling);
+ }
+
+ // Remove the original node's text.
+ node.set_node_value("");
}
-
- // Add our new nodes.
- WebKit.DOM.Node? sibling = node.get_next_sibling();
- for (int i = 0; i < new_nodes.size; i++) {
- WebKit.DOM.Node new_node = new_nodes.get(i);
- if (sibling == null)
- node.get_parent_node().append_child(new_node);
- else
- node.get_parent_node().insert_before(new_node, sibling);
- }
-
- // Remove the original node's text.
- node.set_node_value("");
+ } catch (Error e) {
+ debug("Error linkifying outgoing mail: %s", e.message);
}
- } catch (Error e) {
- debug("Error linkifying outgoing mail: %s", e.message);
}
- }
-
- // Visit children.
- WebKit.DOM.NodeList list = node.get_child_nodes();
- for (int i = 0; i < list.length; i++) {
- linkify_recurse(document, list.item(i), in_link);
- }
-}
-// Validates a URL. Intended to be used as a RegexEvalCallback.
-// Ensures the URL begins with a valid protocol specifier. (If not, we don't
-// want to linkify it.)
-public bool is_valid_url(MatchInfo match_info, StringBuilder result) {
- try {
- string? url = match_info.fetch(0);
- Regex r = new Regex(PROTOCOL_REGEX, RegexCompileFlags.CASELESS);
-
- result.append(r.match(url) ? "<a href=\"%s\">%s</a>".printf(url, url) : url);
- } catch (Error e) {
- debug("URL parsing error: %s\n", e.message);
+ // Visit children.
+ WebKit.DOM.NodeList list = node.get_child_nodes();
+ for (int i = 0; i < list.length; i++) {
+ linkify_recurse(document, list.item(i), in_link);
+ }
}
- return false; // False to continue processing.
-}
-// Converts plain text emails to something safe and usable in HTML.
-public string linkify_and_escape_plain_text(string input) throws Error {
- // Convert < and > into non-printable characters, and change & to &.
- string output = input.replace("<", " \01 ").replace(">", " \02 ").replace("&", "&");
-
- // Converts text links into HTML hyperlinks.
- Regex r = new Regex(URL_REGEX, RegexCompileFlags.CASELESS);
-
- output = r.replace_eval(output, -1, 0, 0, is_valid_url);
- return output.replace(" \01 ", "<").replace(" \02 ", ">");
-}
+ // Validates a URL. Intended to be used as a RegexEvalCallback.
+ // Ensures the URL begins with a valid protocol specifier. (If not, we don't
+ // want to linkify it.)
+ public bool is_valid_url(MatchInfo match_info, StringBuilder result) {
+ try {
+ string? url = match_info.fetch(0);
+ Regex r = new Regex(PROTOCOL_REGEX, RegexCompileFlags.CASELESS);
-public bool node_is_child_of(WebKit.DOM.Node node, string ancestor_tag) {
- WebKit.DOM.Element? ancestor = node.get_parent_element();
- for (; ancestor != null; ancestor = ancestor.get_parent_element()) {
- if (ancestor.get_tag_name() == ancestor_tag) {
- return true;
+ result.append(r.match(url) ? "<a href=\"%s\">%s</a>".printf(url, url) : url);
+ } catch (Error e) {
+ debug("URL parsing error: %s\n", e.message);
}
+ return false; // False to continue processing.
}
- return false;
-}
-public WebKit.DOM.HTMLElement? closest_ancestor(WebKit.DOM.Element element, string selector) {
- try {
- WebKit.DOM.Element? parent = element.get_parent_element();
- while (parent != null && !parent.webkit_matches_selector(selector)) {
- parent = parent.get_parent_element();
- }
- return parent as WebKit.DOM.HTMLElement;
- } catch (Error error) {
- warning("Failed to find ancestor: %s", error.message);
- return null;
+ // Converts plain text emails to something safe and usable in HTML.
+ public string linkify_and_escape_plain_text(string input) throws Error {
+ // Convert < and > into non-printable characters, and change & to &.
+ string output = input.replace("<", " \01 ").replace(">", " \02 ").replace("&", "&");
+
+ // Converts text links into HTML hyperlinks.
+ Regex r = new Regex(URL_REGEX, RegexCompileFlags.CASELESS);
+
+ output = r.replace_eval(output, -1, 0, 0, is_valid_url);
+ return output.replace(" \01 ", "<").replace(" \02 ", ">");
}
-}
-public bool is_descendant_of(WebKit.DOM.Element? element, string selector) {
- try {
- while (element != null) {
- if (element.webkit_matches_selector(selector))
+ public bool node_is_child_of(WebKit.DOM.Node node, string ancestor_tag) {
+ WebKit.DOM.Element? ancestor = node.get_parent_element();
+ for (; ancestor != null; ancestor = ancestor.get_parent_element()) {
+ if (ancestor.get_tag_name() == ancestor_tag) {
return true;
- element = element.get_parent_element();
+ }
}
- } catch (Error error) {
- warning("Problem traversing DOM: %s", error.message);
+ return false;
}
- return false;
-}
-public string decorate_quotes(string text) throws Error {
- int level = 0;
- string outtext = "";
- Regex quote_leader = new Regex("^(>)* ?"); // Some > followed by optional space
-
- foreach (string line in text.split("\n")) {
- MatchInfo match_info;
- if (quote_leader.match_all(line, 0, out match_info)) {
- int start, end, new_level;
- match_info.fetch_pos(0, out start, out end);
- new_level = end / 4; // Cast to int removes 0.25 from space at end, if present
- while (new_level > level) {
- outtext += "<blockquote>";
- level += 1;
- }
- while (new_level < level) {
- outtext += "</blockquote>";
- level -= 1;
+ public WebKit.DOM.HTMLElement? closest_ancestor(WebKit.DOM.Element element, string selector) {
+ try {
+ WebKit.DOM.Element? parent = element.get_parent_element();
+ while (parent != null && !parent.webkit_matches_selector(selector)) {
+ parent = parent.get_parent_element();
}
- outtext += line.substring(end);
- } else {
- debug("This line didn't match the quote regex: %s", line);
- outtext += line;
+ return parent as WebKit.DOM.HTMLElement;
+ } catch (Error error) {
+ warning("Failed to find ancestor: %s", error.message);
+ return null;
}
}
- // Close any remaining blockquotes.
- while (level > 0) {
- outtext += "</blockquote>";
- level -= 1;
+
+ public bool is_descendant_of(WebKit.DOM.Element? element, string selector) {
+ try {
+ while (element != null) {
+ if (element.webkit_matches_selector(selector))
+ return true;
+ element = element.get_parent_element();
+ }
+ } catch (Error error) {
+ warning("Problem traversing DOM: %s", error.message);
+ }
+ return false;
}
- return outtext;
-}
-// 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 "";
+ public string decorate_quotes(string text) throws Error {
+ int level = 0;
+ string outtext = "";
+ Regex quote_leader = new Regex("^(>)* ?"); // Some > followed by optional space
+
+ foreach (string line in text.split("\n")) {
+ MatchInfo match_info;
+ if (quote_leader.match_all(line, 0, out match_info)) {
+ int start, end, new_level;
+ match_info.fetch_pos(0, out start, out end);
+ new_level = end / 4; // Cast to int removes 0.25 from space at end, if present
+ while (new_level > level) {
+ outtext += "<blockquote>";
+ level += 1;
+ }
+ while (new_level < level) {
+ outtext += "</blockquote>";
+ level -= 1;
+ }
+ outtext += line.substring(end);
+ } else {
+ debug("This line didn't match the quote regex: %s", line);
+ outtext += line;
+ }
+ }
+ // Close any remaining blockquotes.
+ while (level > 0) {
+ outtext += "</blockquote>";
+ level -= 1;
+ }
+ return outtext;
}
-
- 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.");
-
+
+ // 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 {
- bq.set_inner_text(@"$QUOTE_START$i$QUOTE_END");
+ el.set_inner_html(saved_doc);
} catch (Error error) {
- debug("Error manipulating DOM: %s", error.message);
+ 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 {
+ if (quote_level == 0 && (line.has_prefix(">") || line.has_prefix("From")))
+ line = " " + line;
+
+ int cut_ind = line.length;
+ if (cut_ind > max_len) {
+ string beg = line[0:max_len];
+ cut_ind = beg.last_index_of(" ") + 1;
+ if (cut_ind == 0) {
+ cut_ind = line.index_of(" ") + 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;
}
-
- // 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);
+
+ 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);
}
-
- // 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 {
- if (quote_level == 0 && (line.has_prefix(">") || line.has_prefix("From")))
- line = " " + line;
-
- int cut_ind = line.length;
- if (cut_ind > max_len) {
- string beg = line[0:max_len];
- cut_ind = beg.last_index_of(" ") + 1;
- if (cut_ind == 0) {
- cut_ind = line.index_of(" ") + 1;
- if (cut_ind == 0)
- cut_ind = line.length;
- if (cut_ind > 998 - prefix.length)
- cut_ind = 998 - prefix.length;
+
+ 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("");
}
- }
- flowed.append(prefix + line[0:cut_ind] + "\n");
- line = line[cut_ind:line.length];
- } while (line.length > 0);
+ return false;
+ });
+ } catch (Error error) {
+ debug("Regex error in denesting blockquotes: %s", error.message);
+ return "";
+ }
}
-
- 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);
-}
+ // Returns a URI suitable for an IMG SRC attribute (or elsewhere, potentially) that is the
+ // memory buffer unpacked into a Base-64 encoded data: URI
+ public string assemble_data_uri(string mimetype, Geary.Memory.Buffer buffer) {
+ // attempt to use UnownedBytesBuffer to avoid memcpying a potentially huge buffer only to
+ // free it when the encoding operation is completed
+ string base64;
+ Geary.Memory.UnownedBytesBuffer? unowned_bytes = buffer as Geary.Memory.UnownedBytesBuffer;
+ if (unowned_bytes != null)
+ base64 = Base64.encode(unowned_bytes.to_unowned_uint8_array());
+ else
+ base64 = Base64.encode(buffer.get_uint8_array());
-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 "";
+ return "data:%s;base64,%s".printf(mimetype, base64);
}
-}
-// Returns a URI suitable for an IMG SRC attribute (or elsewhere, potentially) that is the
-// memory buffer unpacked into a Base-64 encoded data: URI
-public string assemble_data_uri(string mimetype, Geary.Memory.Buffer buffer) {
- // attempt to use UnownedBytesBuffer to avoid memcpying a potentially huge buffer only to
- // free it when the encoding operation is completed
- string base64;
- Geary.Memory.UnownedBytesBuffer? unowned_bytes = buffer as Geary.Memory.UnownedBytesBuffer;
- if (unowned_bytes != null)
- base64 = Base64.encode(unowned_bytes.to_unowned_uint8_array());
- else
- base64 = Base64.encode(buffer.get_uint8_array());
-
- return "data:%s;base64,%s".printf(mimetype, base64);
-}
+ // Turns the data: URI created by assemble_data_uri() back into its components. The returned
+ // buffer is decoded.
+ //
+ // TODO: Return mimetype
+ public bool disassemble_data_uri(string uri, out Geary.Memory.Buffer? buffer) {
+ buffer = null;
-// Turns the data: URI created by assemble_data_uri() back into its components. The returned
-// buffer is decoded.
-//
-// TODO: Return mimetype
-public bool dissasemble_data_uri(string uri, out Geary.Memory.Buffer? buffer) {
- buffer = null;
-
- if (!uri.has_prefix("data:"))
- return false;
-
- // count from semicolon past encoding type specifier
- int start_index = uri.index_of(";");
- if (start_index <= 0)
- return false;
-
- // watch for string termination to avoid overflow
- int base64_len = "base64,".length;
- for (int ctr = 0; ctr < base64_len; ctr++) {
- if (uri[start_index++] == Geary.String.EOS)
+ if (!uri.has_prefix("data:"))
return false;
+
+ // count from semicolon past encoding type specifier
+ int start_index = uri.index_of(";");
+ if (start_index <= 0)
+ return false;
+
+ // watch for string termination to avoid overflow
+ int base64_len = "base64,".length;
+ for (int ctr = 0; ctr < base64_len; ctr++) {
+ if (uri[start_index++] == Geary.String.EOS)
+ return false;
+ }
+
+ // avoid a memory copy of the substring by manually calculating the start address
+ uint8[] bytes = Base64.decode((string) (((char *) uri) + start_index));
+
+ // transfer ownership of the byte array directly to the Buffer; this prevents an
+ // unnecessary copy ... save length before transferring ownership (which frees the array)
+ int bytes_length = bytes.length;
+ buffer = new Geary.Memory.ByteBuffer.take((owned) bytes, bytes_length);
+
+ return true;
}
-
- // avoid a memory copy of the substring by manually calculating the start address
- uint8[] bytes = Base64.decode((string) (((char *) uri) + start_index));
-
- // transfer ownership of the byte array directly to the Buffer; this prevents an
- // unnecessary copy ... save length before transferring ownership (which frees the array)
- int bytes_length = bytes.length;
- buffer = new Geary.Memory.ByteBuffer.take((owned) bytes, bytes_length);
-
- return true;
-}
-// Escape reserved HTML entities if the string does not have HTML tags. If there are no tags,
-// or if preserve_whitespace_in_html is true, wrap the string a div to preserve whitespace.
-public string smart_escape(string? text, bool preserve_whitespace_in_html) {
- if (text == null)
- return text;
-
- string res = text;
- if (!Regex.match_simple("<([A-Z]*)(?: [^>]*)?>.*</(\\1)>|<[A-Z]*(?: [^>]*)?/>", res,
- RegexCompileFlags.CASELESS)) {
- res = Geary.HTML.escape_markup(res);
- preserve_whitespace_in_html = true;
+ // Escape reserved HTML entities if the string does not have HTML tags. If there are no tags,
+ // or if preserve_whitespace_in_html is true, wrap the string a div to preserve whitespace.
+ public string smart_escape(string? text, bool preserve_whitespace_in_html) {
+ if (text == null)
+ return text;
+
+ string res = text;
+ if (!Regex.match_simple("<([A-Z]*)(?: [^>]*)?>.*</(\\1)>|<[A-Z]*(?: [^>]*)?/>", res,
+ RegexCompileFlags.CASELESS)) {
+ res = Geary.HTML.escape_markup(res);
+ preserve_whitespace_in_html = true;
+ }
+ if (preserve_whitespace_in_html)
+ res = @"<div style='white-space: pre;'>$res</div>";
+ return res;
}
- if (preserve_whitespace_in_html)
- res = @"<div style='white-space: pre;'>$res</div>";
- return res;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]