use std::fmt;
use std::error;
use webcore::value::{Reference, FromReference};
use webcore::try_from::TryInto;
use webapi::event_target::{IEventTarget, EventTarget};
use webapi::node_list::NodeList;
#[derive(Debug)]
pub struct NotFoundError( String );
impl error::Error for NotFoundError {
fn description( &self ) -> &str {
self.0.as_str()
}
}
impl fmt::Display for NotFoundError {
fn fmt( &self, formatter: &mut fmt::Formatter ) -> fmt::Result {
write!( formatter, "{}", self.0 )
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum CloneKind {
Shallow,
Deep
}
pub trait INode: IEventTarget + FromReference {
fn append_child< T: INode >( &self, child: &T ) {
js! { @(no_return)
@{self.as_ref()}.appendChild( @{child.as_ref()} );
}
}
fn remove_child< T: INode >( &self, child: &T ) -> Result< (), NotFoundError > {
let status = js! {
try {
@{self.as_ref()}.removeChild( @{child.as_ref()} );
return true;
} catch( exception ) {
if( exception instanceof NotFoundError ) {
return false;
} else {
throw exception;
}
}
};
if status == true {
Ok(())
} else {
Err( NotFoundError( "The node to be removed is not a child of this node.".to_owned() ) )
}
}
fn clone_node( &self, kind: CloneKind ) -> Self {
let is_deep = match kind {
CloneKind::Deep => true,
CloneKind::Shallow => false
};
let cloned = js! {
return @{self.as_ref()}.cloneNode( @{is_deep} );
};
cloned.into_reference().unwrap().downcast::< Self >().unwrap()
}
fn contains< T: INode >( &self, node: &T ) -> bool {
js!(
return @{self.as_ref()}.contains( @{node.as_ref()} );
).try_into().unwrap()
}
fn insert_before< T: INode, U: INode >( &self, new_node: &T, reference_node: &U ) {
js! { @(no_return)
@{self.as_ref()}.insertBefore( @{new_node.as_ref()}, @{reference_node.as_ref()} );
}
}
fn replace_child< T: INode, U: INode >( &self, new_child: &T, old_child: &U ) {
js! { @(no_return)
@{self.as_ref()}.replaceChild( @{new_child.as_ref()}, @{old_child.as_ref()} );
}
}
fn parent_node( &self ) -> Option< Node > {
js!(
return @{self.as_ref()}.parentNode;
).try_into().ok()
}
fn first_child( &self ) -> Option< Node > {
js!(
return @{self.as_ref()}.firstChild;
).try_into().ok()
}
fn inner_text( &self ) -> String {
js!(
return @{self.as_ref()}.innerText;
).try_into().unwrap()
}
fn text_content( &self ) -> Option< String > {
js!(
return @{self.as_ref()}.textContent;
).try_into().unwrap()
}
fn set_text_content( &self, text: &str ) {
js! { @(no_return)
@{self.as_ref()}.textContent = @{text};
}
}
fn child_nodes( &self ) -> NodeList {
unsafe {
js!(
return @{self.as_ref()}.childNodes;
).into_reference_unchecked().unwrap()
}
}
}
pub struct Node( Reference );
impl IEventTarget for Node {}
impl INode for Node {}
reference_boilerplate! {
Node,
instanceof Node
convertible to EventTarget
}