use std::fmt::{Display, Error, Formatter};
use std::marker::PhantomData;
use stdweb::web::event::*;
use stdweb::web::{self, Element, EventListenerHandle, IElement, IEventTarget, INode};
use crate::OutputType;
use crate::dom::VNode;
use crate::events::{EventHandler, IntoEventHandler};
pub struct Stdweb;
impl OutputType for Stdweb {
type Events = Events;
type EventTarget = Element;
type EventListenerHandle = EventListenerHandle;
}
macro_rules! declare_events {
($($name:ident : $type:ty ,)*) => {
pub struct Events {
$(
pub $name: Option<Box<dyn EventHandler<Stdweb, $type>>>,
)*
}
impl Default for Events {
fn default() -> Self {
Events {
$(
$name: None,
)*
}
}
}
#[macro_export]
macro_rules! for_events {
($event:ident in $events:expr => $body:block) => {
$(
if let Some(ref mut $event) = $events.$name $body
)*
}
}
}
}
declare_events! {
abort: ResourceAbortEvent,
blur: BlurEvent,
change: ChangeEvent,
click: ClickEvent,
contextmenu: ContextMenuEvent,
dblclick: DoubleClickEvent,
drag: DragEvent,
dragend: DragEndEvent,
dragenter: DragEnterEvent,
dragexit: DragExitEvent,
dragleave: DragLeaveEvent,
dragover: DragOverEvent,
dragstart: DragStartEvent,
drop: DragDropEvent,
error: ResourceErrorEvent,
focus: FocusEvent,
input: InputEvent,
keydown: KeyDownEvent,
keypress: KeyPressEvent,
keyup: KeyUpEvent,
load: ResourceLoadEvent,
loadstart: LoadStartEvent,
mousedown: MouseDownEvent,
mouseenter: MouseEnterEvent,
mouseleave: MouseLeaveEvent,
mousemove: MouseMoveEvent,
mouseout: MouseOutEvent,
mouseover: MouseOverEvent,
mouseup: MouseUpEvent,
mousewheel: MouseWheelEvent,
progress: ProgressEvent,
resize: ResizeEvent,
scroll: ScrollEvent,
submit: SubmitEvent,
}
impl Display for Events {
fn fmt(&self, _f: &mut Formatter) -> Result<(), Error> {
Ok(())
}
}
pub struct EFn<F, E>(Option<F>, PhantomData<E>);
impl<F, E> EFn<F, E>
where
F: FnMut(E) + 'static,
{
pub fn new(f: F) -> Self {
EFn(Some(f), PhantomData)
}
}
impl<F, E> IntoEventHandler<Stdweb, E> for F
where
F: FnMut(E) + 'static,
E: ConcreteEvent + 'static,
{
fn into_event_handler(self) -> Box<dyn EventHandler<Stdweb, E>> {
Box::new(EFn::new(self))
}
}
impl<F, E> IntoEventHandler<Stdweb, E> for EFn<F, E>
where
F: FnMut(E) + 'static,
E: ConcreteEvent + 'static,
{
fn into_event_handler(self) -> Box<dyn EventHandler<Stdweb, E>> {
Box::new(self)
}
}
impl<F, E> EventHandler<Stdweb, E> for EFn<F, E>
where
F: FnMut(E) + 'static,
E: ConcreteEvent + 'static,
{
fn attach(&mut self, target: &mut <Stdweb as OutputType>::EventTarget) -> EventListenerHandle {
let handler = self.0.take().unwrap();
target.add_event_listener(handler)
}
fn render(&self) -> Option<String> {
None
}
}
impl Stdweb {
pub fn install_handlers(target: &mut Element, handlers: &mut Events) {
for_events!(handler in handlers => {
handler.attach(target);
});
}
pub fn build(
document: &web::Document,
vnode: VNode<'_, Stdweb>,
) -> Result<web::Node, web::error::InvalidCharacterError> {
match vnode {
VNode::Text(text) => Ok(document.create_text_node(&text).into()),
VNode::Element(element) => {
let mut node = document.create_element(element.name)?;
for (key, value) in element.attributes {
node.set_attribute(&key, &value)?;
}
Stdweb::install_handlers(&mut node, element.events);
for child in element.children {
let child_node = Stdweb::build(document, child)?;
node.append_child(&child_node);
}
Ok(node.into())
}
}
}
}