use super::network::SocialNetwork;
use super::{MJSocialElement, MJSocialElementChild, NAME};
use crate::helper::size::{Pixel, Size};
use crate::helper::tag::Tag;
use crate::prelude::render::{Error, Header, Options, Render, Renderable};
use std::cell::{Ref, RefCell};
use std::collections::HashMap;
use std::rc::Rc;
const DEFAULT_ICON_ORIGIN: &str = "https://www.mailjet.com/images/theme/v1/icons/ico-social/";
impl<'r, 'e: 'r, 'h: 'r> Renderable<'r, 'e, 'h> for MJSocialElementChild {
fn renderer(&'e self, header: Rc<RefCell<Header<'h>>>) -> Box<dyn Render<'h> + 'r> {
match self {
Self::Text(elt) => elt.renderer(header),
Self::Comment(elt) => elt.renderer(header),
}
}
}
struct MJSocialElementRender<'e, 'h> {
header: Rc<RefCell<Header<'h>>>,
element: &'e MJSocialElement,
extra: HashMap<String, String>,
container_width: Option<Pixel>,
network: Option<SocialNetwork>,
}
impl<'e, 'h> MJSocialElementRender<'e, 'h> {
fn get_background_color(&self) -> Option<String> {
self.attribute("background-color").or_else(|| {
self.network
.as_ref()
.map(|net| net.background_color().to_string())
})
}
fn get_icon_size(&self) -> Option<Size> {
self.attribute_as_size("icon-size")
}
fn get_icon_height(&self) -> Option<Size> {
self.attribute_as_size("icon-height")
}
fn get_icon_src(&self, opts: &Options) -> Option<String> {
self.attribute("src").or_else(|| {
self.network.as_ref().map(|net| {
if let Some(ref origin) = opts.social_icon_origin {
net.icon_src(origin)
} else {
net.icon_src(DEFAULT_ICON_ORIGIN)
}
})
})
}
fn set_style_img(&self, tag: Tag) -> Tag {
tag.maybe_add_style("border-radius", self.attribute("border-radius"))
.add_style("display", "block")
}
fn set_style_icon(&self, tag: Tag) -> Tag {
tag.maybe_add_style("padding", self.attribute("icon-padding"))
.add_style("font-size", "0")
.maybe_add_style(
"height",
self.get_icon_height()
.or_else(|| self.get_icon_size())
.map(|item| item.to_string()),
)
.add_style("vertical-align", "middle")
.maybe_add_style("width", self.get_icon_size().map(|item| item.to_string()))
}
fn set_style_table(&self, tag: Tag) -> Tag {
tag.maybe_add_style("background", self.get_background_color())
.maybe_add_style("border-radius", self.attribute("border-radius"))
.maybe_add_style("width", self.get_icon_size().map(|size| size.to_string()))
}
fn set_style_td(&self, tag: Tag) -> Tag {
tag.maybe_add_style("padding", self.attribute("padding"))
.maybe_add_style("vertical-align", self.attribute("vertical-align"))
}
fn set_style_td_text(&self, tag: Tag) -> Tag {
tag.add_style("vertical-align", "middle")
.maybe_add_style("padding", self.attribute("text-padding"))
}
fn set_style_text(&self, tag: Tag) -> Tag {
tag.maybe_add_style("color", self.attribute("color"))
.maybe_add_style("font-size", self.attribute("font-size"))
.maybe_add_style("font-weight", self.attribute("font-weight"))
.maybe_add_style("font-style", self.attribute("font-style"))
.maybe_add_style("font-family", self.attribute("font-family"))
.maybe_add_style("line-height", self.attribute("line-height"))
.maybe_add_style("text-decoration", self.attribute("text-decoration"))
}
fn get_href(&self) -> Option<String> {
self.attribute("href")
.map(|href| {
self.network
.as_ref()
.and_then(|net| net.share_url(&href))
.or(Some(href))
})
.unwrap_or_default()
}
fn render_icon(&self, href: &Option<String>, opts: &Options) -> String {
let table = self.set_style_table(Tag::table_presentation());
let tr = Tag::tr();
let td = self.set_style_icon(Tag::td());
let a = Tag::new("a")
.maybe_add_attribute("href", href.as_ref())
.maybe_add_attribute("rel", self.attribute("rel"))
.maybe_add_attribute("target", self.attribute("target"));
let img = self
.set_style_img(Tag::new("img"))
.maybe_add_attribute("alt", self.attribute("alt"))
.maybe_add_attribute("title", self.attribute("title"))
.maybe_add_attribute(
"height",
self.get_icon_height()
.or_else(|| self.get_icon_size())
.map(|size| size.value().to_string()),
)
.maybe_add_attribute("src", self.get_icon_src(opts))
.maybe_add_attribute(
"width",
self.get_icon_size().map(|size| size.value().to_string()),
);
table.render(tr.render(td.render(if href.is_some() {
a.render(img.closed())
} else {
img.closed()
})))
}
fn render_text(&self, href: &Option<String>, opts: &Options) -> Result<String, Error> {
let td = self.set_style_td_text(Tag::td());
let wrapper = if href.is_some() {
Tag::new("a")
.maybe_add_attribute("href", href.as_ref())
.maybe_add_attribute("rel", self.attribute("rel"))
.maybe_add_attribute("target", self.attribute("target"))
} else {
Tag::new("span")
};
let wrapper = self.set_style_text(wrapper);
let content = self
.element
.children
.iter()
.try_fold(String::default(), |res, child| {
let renderer = child.renderer(Rc::clone(&self.header));
Ok(res + &renderer.render(opts)?)
})?;
Ok(td.render(wrapper.render(content)))
}
}
impl<'e, 'h> Render<'h> for MJSocialElementRender<'e, 'h> {
fn default_attribute(&self, key: &str) -> Option<&str> {
match key {
"align" => Some("left"),
"color" => Some("#000"),
"border-radius" => Some("3px"),
"font-family" => Some("Ubuntu, Helvetica, Arial, sans-serif"),
"font-size" => Some("13px"),
"line-height" => Some("1"),
"padding" => Some("4px"),
"target" => Some("_blank"),
"text-decoration" => Some("none"),
"vertical-align" => Some("middle"),
_ => None,
}
}
fn add_extra_attribute(&mut self, key: &str, value: &str) {
self.extra.insert(key.to_string(), value.to_string());
}
fn extra_attributes(&self) -> Option<&HashMap<String, String>> {
Some(&self.extra)
}
fn attributes(&self) -> Option<&HashMap<String, String>> {
Some(&self.element.attributes)
}
fn tag(&self) -> Option<&str> {
Some(NAME)
}
fn set_container_width(&mut self, width: Option<Pixel>) {
self.container_width = width;
}
fn header(&self) -> Ref<Header<'h>> {
self.header.borrow()
}
fn render(&self, opts: &Options) -> Result<String, Error> {
let href = self.get_href();
let tr = Tag::tr().maybe_add_class(self.attribute("css-class"));
let td = self.set_style_td(Tag::td());
let mut res = td.render(self.render_icon(&href, opts));
if !self.element.children.is_empty() {
res.push_str(&self.render_text(&href, opts)?);
}
Ok(tr.render(res))
}
}
impl<'r, 'e: 'r, 'h: 'r> Renderable<'r, 'e, 'h> for MJSocialElement {
fn renderer(&'e self, header: Rc<RefCell<Header<'h>>>) -> Box<dyn Render<'h> + 'r> {
Box::new(MJSocialElementRender::<'e, 'h> {
element: self,
header,
extra: HashMap::new(),
container_width: None,
network: self
.attributes
.get("name")
.and_then(|name| SocialNetwork::find(name)),
})
}
}