Reorganise, docs, HTML escaping.
This commit is contained in:
parent
7d1e95f262
commit
cc8e7219a2
|
@ -28,16 +28,12 @@ impl Declare {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn elem_name(&self) -> TokenTree {
|
fn elem_name(&self) -> TokenTree {
|
||||||
Ident::new(
|
Ident::new(&self.name.to_string(), self.name.span()).into()
|
||||||
&format!("Element_{}", self.name.to_string()),
|
|
||||||
self.name.span(),
|
|
||||||
)
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn attr_type_name(&self) -> TokenTree {
|
fn attr_type_name(&self) -> TokenTree {
|
||||||
Ident::new(
|
Ident::new(
|
||||||
&format!("ElementAttrs_{}", self.name.to_string()),
|
&format!("Attrs_{}", self.name.to_string()),
|
||||||
self.name.span(),
|
self.name.span(),
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
|
@ -57,7 +53,7 @@ impl Declare {
|
||||||
let child_name: TokenTree =
|
let child_name: TokenTree =
|
||||||
Ident::new(&format!("child_{}", child.to_string()), child.span()).into();
|
Ident::new(&format!("child_{}", child.to_string()), child.span()).into();
|
||||||
let child_type: TokenTree =
|
let child_type: TokenTree =
|
||||||
Ident::new(&format!("Element_{}", child.to_string()), child.span()).into();
|
Ident::new(&format!("{}", child.to_string()), child.span()).into();
|
||||||
let child_str = Literal::string(&child.to_string()).into();
|
let child_str = Literal::string(&child.to_string()).into();
|
||||||
(child_name, child_type, child_str)
|
(child_name, child_type, child_str)
|
||||||
})
|
})
|
||||||
|
@ -107,7 +103,7 @@ impl Declare {
|
||||||
quote!(
|
quote!(
|
||||||
pub struct $elem_name {
|
pub struct $elem_name {
|
||||||
pub attrs: $attr_type_name,
|
pub attrs: $attr_type_name,
|
||||||
pub data_attributes: std::collections::BTreeMap<String, String>,
|
pub data_attributes: Vec<(String, String)>,
|
||||||
$body
|
$body
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -131,7 +127,7 @@ impl Declare {
|
||||||
body.extend(quote!(
|
body.extend(quote!(
|
||||||
attrs: $attr_type_name { $attrs },
|
attrs: $attr_type_name { $attrs },
|
||||||
));
|
));
|
||||||
body.extend(quote!(data_attributes: std::collections::BTreeMap::new(),));
|
body.extend(quote!(data_attributes: Vec::new(),));
|
||||||
for (child_name, _, _) in self.req_children() {
|
for (child_name, _, _) in self.req_children() {
|
||||||
body.extend(quote!( $child_name, ));
|
body.extend(quote!( $child_name, ));
|
||||||
}
|
}
|
||||||
|
@ -185,7 +181,7 @@ impl Declare {
|
||||||
$req_children
|
$req_children
|
||||||
$opt_children
|
$opt_children
|
||||||
|
|
||||||
::elements::VNode::Element(::elements::VElement {
|
::dom::VNode::Element(::dom::VElement {
|
||||||
name: $elem_name,
|
name: $elem_name,
|
||||||
attributes,
|
attributes,
|
||||||
children
|
children
|
||||||
|
@ -197,8 +193,8 @@ impl Declare {
|
||||||
let elem_name = self.elem_name();
|
let elem_name = self.elem_name();
|
||||||
let vnode = self.impl_vnode();
|
let vnode = self.impl_vnode();
|
||||||
quote!(
|
quote!(
|
||||||
impl ::elements::Node for $elem_name {
|
impl ::dom::Node for $elem_name {
|
||||||
fn vnode(&self) -> ::elements::VNode {
|
fn vnode(&self) -> ::dom::VNode {
|
||||||
$vnode
|
$vnode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -225,7 +221,7 @@ impl Declare {
|
||||||
}
|
}
|
||||||
|
|
||||||
quote!(
|
quote!(
|
||||||
impl ::elements::Element for $elem_name {
|
impl ::dom::Element for $elem_name {
|
||||||
fn name() -> &'static str {
|
fn name() -> &'static str {
|
||||||
$name
|
$name
|
||||||
}
|
}
|
||||||
|
@ -306,7 +302,8 @@ impl Declare {
|
||||||
for (attr_name, _, attr_str) in self.attrs() {
|
for (attr_name, _, attr_str) in self.attrs() {
|
||||||
print_attrs.extend(quote!(
|
print_attrs.extend(quote!(
|
||||||
if let Some(ref value) = self.attrs.$attr_name {
|
if let Some(ref value) = self.attrs.$attr_name {
|
||||||
write!(f, " {}={:?}", $attr_str, value.to_string())?;
|
write!(f, " {}=\"{}\"", $attr_str,
|
||||||
|
::htmlescape::encode_attribute(&value.to_string()))?;
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -317,7 +314,8 @@ impl Declare {
|
||||||
write!(f, "<{}", $name)?;
|
write!(f, "<{}", $name)?;
|
||||||
$print_attrs
|
$print_attrs
|
||||||
for (key, value) in &self.data_attributes {
|
for (key, value) in &self.data_attributes {
|
||||||
write!(f, " data-{}={:?}", key, value)?;
|
write!(f, " data-{}=\"{}\"", key,
|
||||||
|
::htmlescape::encode_attribute(&value))?;
|
||||||
}
|
}
|
||||||
$print_children
|
$print_children
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ impl Node {
|
||||||
Node::Element(el) => el.into_token_stream(),
|
Node::Element(el) => el.into_token_stream(),
|
||||||
Node::Text(text) => {
|
Node::Text(text) => {
|
||||||
let text = TokenTree::Literal(text);
|
let text = TokenTree::Literal(text);
|
||||||
quote!(Box::new(typed_html::elements::TextNode::new($text.to_string())))
|
quote!(Box::new(typed_html::dom::TextNode::new($text.to_string())))
|
||||||
}
|
}
|
||||||
Node::Block(_) => panic!("cannot have a block in this position"),
|
Node::Block(_) => panic!("cannot have a block in this position"),
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,7 @@ impl Element {
|
||||||
fn into_token_stream(mut self) -> TokenStream {
|
fn into_token_stream(mut self) -> TokenStream {
|
||||||
let name = self.name;
|
let name = self.name;
|
||||||
let name_str = name.to_string();
|
let name_str = name.to_string();
|
||||||
let typename: TokenTree = Ident::new(&format!("Element_{}", &name_str), name.span()).into();
|
let typename: TokenTree = Ident::new(&name_str, name.span()).into();
|
||||||
let req_names = required_children(&name_str);
|
let req_names = required_children(&name_str);
|
||||||
if req_names.len() > self.children.len() {
|
if req_names.len() > self.children.len() {
|
||||||
Diagnostic::spanned(
|
Diagnostic::spanned(
|
||||||
|
@ -164,7 +164,7 @@ impl Element {
|
||||||
.map(|(k, v)| (TokenTree::from(Literal::string(&k)), v.clone()))
|
.map(|(k, v)| (TokenTree::from(Literal::string(&k)), v.clone()))
|
||||||
{
|
{
|
||||||
body.extend(quote!(
|
body.extend(quote!(
|
||||||
element.data_attributes.insert($key.into(), $value.into());
|
element.data_attributes.push(($key.into(), $value.into()));
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
body.extend(opt_children);
|
body.extend(opt_children);
|
||||||
|
|
|
@ -11,3 +11,4 @@ mime = "0.3.12"
|
||||||
language-tags = "0.2.2"
|
language-tags = "0.2.2"
|
||||||
enumset = "0.3.12"
|
enumset = "0.3.12"
|
||||||
http = "0.1.13"
|
http = "0.1.13"
|
||||||
|
htmlescape = "0.3.1"
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
extern crate typed_html;
|
extern crate typed_html;
|
||||||
extern crate typed_html_macros;
|
extern crate typed_html_macros;
|
||||||
|
|
||||||
use typed_html::elements::Node;
|
use typed_html::dom::Node;
|
||||||
use typed_html::types::*;
|
use typed_html::types::*;
|
||||||
use typed_html_macros::html;
|
use typed_html_macros::html;
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ fn main() {
|
||||||
html!(<p>{ text!("{}. Ceci n'est pas une chatte.", i) }</p>)
|
html!(<p>{ text!("{}. Ceci n'est pas une chatte.", i) }</p>)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
<p>"<img src=\"javascript:alert('pwned lol')\">"</p>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,160 @@
|
||||||
|
//! DOM and virtual DOM types.
|
||||||
|
|
||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use elements::{FlowContent, PhrasingContent};
|
||||||
|
use htmlescape::encode_minimal;
|
||||||
|
|
||||||
|
/// An untyped representation of an HTML node.
|
||||||
|
///
|
||||||
|
/// This structure is designed to be easily walked in order to render a DOM tree
|
||||||
|
/// or diff against an existing tree. It's the stringly typed version of
|
||||||
|
/// [`Node`][Node].
|
||||||
|
///
|
||||||
|
/// It can be constructed from any ['Node'][Node]:
|
||||||
|
///
|
||||||
|
/// ```no_compile
|
||||||
|
/// html!(
|
||||||
|
/// <p>"But how does she "<em>"eat?"</em></p>
|
||||||
|
/// ).vnode()
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [Node]: trait.Node.html
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum VNode {
|
||||||
|
Text(String),
|
||||||
|
Element(VElement),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An untyped representation of an HTML element.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct VElement {
|
||||||
|
pub name: &'static str,
|
||||||
|
pub attributes: Vec<(String, String)>,
|
||||||
|
pub children: Vec<VNode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trait for rendering a typed HTML node.
|
||||||
|
///
|
||||||
|
/// All [HTML elements][elements] implement this, in addition to
|
||||||
|
/// [`TextNode`][TextNode].
|
||||||
|
///
|
||||||
|
/// It implements [`Display`][Display] for rendering to strings, and the
|
||||||
|
/// [`vnode()`][vnode] method can be used to render a virtual DOM structure.
|
||||||
|
///
|
||||||
|
/// [Display]: https://doc.rust-lang.org/std/fmt/trait.Display.html
|
||||||
|
/// [TextNode]: struct.TextNode.html
|
||||||
|
/// [elements]: ../elements/index.html
|
||||||
|
/// [vnode]: #tymethod.vnode
|
||||||
|
pub trait Node: Display {
|
||||||
|
/// Render the node into a [`VNode`][VNode] tree.
|
||||||
|
///
|
||||||
|
/// [VNode]: enum.VNode.html
|
||||||
|
fn vnode(&self) -> VNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trait for querying a typed HTML element.
|
||||||
|
///
|
||||||
|
/// All [HTML elements][elements] implement this.
|
||||||
|
///
|
||||||
|
/// [elements]: ../elements/index.html
|
||||||
|
pub trait Element: Node {
|
||||||
|
/// Get the name of the element.
|
||||||
|
fn name() -> &'static str;
|
||||||
|
/// Get a list of the attribute names for this element.
|
||||||
|
///
|
||||||
|
/// This includes only the typed attributes, not any `data-` attributes
|
||||||
|
/// defined on this particular element instance.
|
||||||
|
///
|
||||||
|
/// This is probably not useful unless you're the `html!` macro.
|
||||||
|
fn attribute_names() -> &'static [&'static str];
|
||||||
|
/// Get a list of the element names of required children for this element.
|
||||||
|
///
|
||||||
|
/// This is probably not useful unless you're the `html!` macro.
|
||||||
|
fn required_children() -> &'static [&'static str];
|
||||||
|
/// Get a list of the defined attribute pairs for this element.
|
||||||
|
///
|
||||||
|
/// This will convert attribute values into strings and return a vector of
|
||||||
|
/// key/value pairs.
|
||||||
|
fn attributes(&self) -> Vec<(String, String)>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An HTML text node.
|
||||||
|
pub struct TextNode(String);
|
||||||
|
|
||||||
|
/// Macro for creating text nodes.
|
||||||
|
///
|
||||||
|
/// Returns a boxed text node of type `Box<TextNode>`.
|
||||||
|
///
|
||||||
|
/// These can be created inside the `html!` macro directly by using string
|
||||||
|
/// literals. This macro is useful for creating text macros inside code blocks.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_compile
|
||||||
|
/// html!(
|
||||||
|
/// <p>{ text!("Hello Joe!") }</p>
|
||||||
|
/// )
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ```no_compile
|
||||||
|
/// html!(
|
||||||
|
/// <p>{ text!("Hello {}!", "Robert") }</p>
|
||||||
|
/// )
|
||||||
|
/// ```
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! text {
|
||||||
|
($t:expr) => {
|
||||||
|
Box::new($crate::dom::TextNode::new($t))
|
||||||
|
};
|
||||||
|
($format:tt, $($tail:tt),*) => {
|
||||||
|
Box::new($crate::dom::TextNode::new(format!($format, $($tail),*)))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TextNode {
|
||||||
|
/// Construct a text node.
|
||||||
|
///
|
||||||
|
/// The preferred way to construct a text node is with the [`text!()`][text]
|
||||||
|
/// macro.
|
||||||
|
///
|
||||||
|
/// [text]: ../macro.text.html
|
||||||
|
pub fn new<S: Into<String>>(s: S) -> Self {
|
||||||
|
TextNode(s.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for TextNode {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
||||||
|
f.write_str(&encode_minimal(&self.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node for TextNode {
|
||||||
|
fn vnode(&self) -> VNode {
|
||||||
|
VNode::Text(self.0.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl IntoIterator for TextNode {
|
||||||
|
type Item = TextNode;
|
||||||
|
type IntoIter = std::vec::IntoIter<TextNode>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
vec![self].into_iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoIterator for Box<TextNode> {
|
||||||
|
type Item = Box<TextNode>;
|
||||||
|
type IntoIter = std::vec::IntoIter<Box<TextNode>>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
vec![self].into_iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FlowContent for TextNode {}
|
||||||
|
impl PhrasingContent for TextNode {}
|
|
@ -1,34 +1,11 @@
|
||||||
#![allow(non_camel_case_types)]
|
//! Types for all standard HTML5 elements.
|
||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
use std::fmt::Display;
|
|
||||||
use typed_html_macros::declare_elements;
|
use typed_html_macros::declare_elements;
|
||||||
|
|
||||||
use super::types::*;
|
use dom::{Node, TextNode};
|
||||||
|
use types::*;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
// Marker traits for element content groups
|
||||||
pub enum VNode {
|
|
||||||
Text(String),
|
|
||||||
Element(VElement),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct VElement {
|
|
||||||
pub name: &'static str,
|
|
||||||
pub attributes: Vec<(String, String)>,
|
|
||||||
pub children: Vec<VNode>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Node: Display {
|
|
||||||
fn vnode(&self) -> VNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Element: Node {
|
|
||||||
fn name() -> &'static str;
|
|
||||||
fn attribute_names() -> &'static [&'static str];
|
|
||||||
fn required_children() -> &'static [&'static str];
|
|
||||||
fn attributes(&self) -> Vec<(String, String)>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait MetadataContent: Node {}
|
pub trait MetadataContent: Node {}
|
||||||
pub trait FlowContent: Node {}
|
pub trait FlowContent: Node {}
|
||||||
|
@ -49,56 +26,6 @@ pub trait SelectContent: Node {}
|
||||||
pub trait TableContent: Node {}
|
pub trait TableContent: Node {}
|
||||||
pub trait TableColumnContent: Node {}
|
pub trait TableColumnContent: Node {}
|
||||||
|
|
||||||
impl IntoIterator for TextNode {
|
|
||||||
type Item = TextNode;
|
|
||||||
type IntoIter = std::vec::IntoIter<TextNode>;
|
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
vec![self].into_iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoIterator for Box<TextNode> {
|
|
||||||
type Item = Box<TextNode>;
|
|
||||||
type IntoIter = std::vec::IntoIter<Box<TextNode>>;
|
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
vec![self].into_iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TextNode(String);
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! text {
|
|
||||||
($t:expr) => {
|
|
||||||
Box::new($crate::elements::TextNode::new($t))
|
|
||||||
};
|
|
||||||
($format:tt, $($tail:tt),*) => {
|
|
||||||
Box::new($crate::elements::TextNode::new(format!($format, $($tail),*)))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TextNode {
|
|
||||||
pub fn new<S: Into<String>>(s: S) -> Self {
|
|
||||||
TextNode(s.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for TextNode {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
|
||||||
self.0.fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Node for TextNode {
|
|
||||||
fn vnode(&self) -> VNode {
|
|
||||||
VNode::Text(self.0.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl FlowContent for TextNode {}
|
|
||||||
impl PhrasingContent for TextNode {}
|
|
||||||
|
|
||||||
declare_elements!{
|
declare_elements!{
|
||||||
html {
|
html {
|
||||||
xmlns: Uri,
|
xmlns: Uri,
|
||||||
|
@ -188,7 +115,7 @@ declare_elements!{
|
||||||
data {
|
data {
|
||||||
value: String,
|
value: String,
|
||||||
} in [FlowContent, PhrasingContent] with PhrasingContent;
|
} in [FlowContent, PhrasingContent] with PhrasingContent;
|
||||||
datalist in [FlowContent, PhrasingContent] with Element_option;
|
datalist in [FlowContent, PhrasingContent] with option;
|
||||||
del {
|
del {
|
||||||
cite: Uri,
|
cite: Uri,
|
||||||
datetime: Datetime,
|
datetime: Datetime,
|
||||||
|
@ -302,12 +229,12 @@ declare_elements!{
|
||||||
typemustmatch: bool,
|
typemustmatch: bool,
|
||||||
usemap: String, // TODO should be a fragment starting with '#'
|
usemap: String, // TODO should be a fragment starting with '#'
|
||||||
width: usize,
|
width: usize,
|
||||||
} in [FlowContent, PhrasingContent, EmbeddedContent, InteractiveContent, FormContent] with Element_param;
|
} in [FlowContent, PhrasingContent, EmbeddedContent, InteractiveContent, FormContent] with param;
|
||||||
ol {
|
ol {
|
||||||
reversed: bool,
|
reversed: bool,
|
||||||
start: isize,
|
start: isize,
|
||||||
type: OrderedListType,
|
type: OrderedListType,
|
||||||
} in [FlowContent] with Element_li;
|
} in [FlowContent] with li;
|
||||||
output {
|
output {
|
||||||
for: SpacedSet<Id>,
|
for: SpacedSet<Id>,
|
||||||
form: Id,
|
form: Id,
|
||||||
|
@ -373,7 +300,7 @@ declare_elements!{
|
||||||
time {
|
time {
|
||||||
datetime: Datetime,
|
datetime: Datetime,
|
||||||
} in [FlowContent, PhrasingContent] with PhrasingContent;
|
} in [FlowContent, PhrasingContent] with PhrasingContent;
|
||||||
ul in [FlowContent] with Element_li;
|
ul in [FlowContent] with li;
|
||||||
var in [FlowContent, PhrasingContent] with PhrasingContent;
|
var in [FlowContent, PhrasingContent] with PhrasingContent;
|
||||||
video in [FlowContent, PhrasingContent, EmbeddedContent] with MediaContent;
|
video in [FlowContent, PhrasingContent, EmbeddedContent] with MediaContent;
|
||||||
wbr in [FlowContent, PhrasingContent];
|
wbr in [FlowContent, PhrasingContent];
|
||||||
|
@ -396,7 +323,7 @@ declare_elements!{
|
||||||
};
|
};
|
||||||
colgroup {
|
colgroup {
|
||||||
span: usize,
|
span: usize,
|
||||||
} in [TableContent] with Element_col;
|
} in [TableContent] with col;
|
||||||
dd in [DescriptionListContent] with FlowContent;
|
dd in [DescriptionListContent] with FlowContent;
|
||||||
dt in [DescriptionListContent] with FlowContent;
|
dt in [DescriptionListContent] with FlowContent;
|
||||||
figcaption with FlowContent;
|
figcaption with FlowContent;
|
||||||
|
@ -413,7 +340,7 @@ declare_elements!{
|
||||||
optgroup {
|
optgroup {
|
||||||
disabled: bool,
|
disabled: bool,
|
||||||
label: String,
|
label: String,
|
||||||
} in [SelectContent] with Element_option;
|
} in [SelectContent] with option;
|
||||||
param {
|
param {
|
||||||
name: String,
|
name: String,
|
||||||
value: String,
|
value: String,
|
||||||
|
@ -423,13 +350,13 @@ declare_elements!{
|
||||||
type: Mime,
|
type: Mime,
|
||||||
} in [MediaContent];
|
} in [MediaContent];
|
||||||
summary with PhrasingContent;
|
summary with PhrasingContent;
|
||||||
tbody in [TableContent] with Element_tr;
|
tbody in [TableContent] with tr;
|
||||||
td {
|
td {
|
||||||
colspan: usize,
|
colspan: usize,
|
||||||
headers: SpacedSet<Id>,
|
headers: SpacedSet<Id>,
|
||||||
rowspan: usize,
|
rowspan: usize,
|
||||||
} in [TableColumnContent] with FlowContent;
|
} in [TableColumnContent] with FlowContent;
|
||||||
tfoot in [TableContent] with Element_tr;
|
tfoot in [TableContent] with tr;
|
||||||
th {
|
th {
|
||||||
abbr: String,
|
abbr: String,
|
||||||
colspan: usize,
|
colspan: usize,
|
||||||
|
@ -437,7 +364,7 @@ declare_elements!{
|
||||||
rowspan: usize,
|
rowspan: usize,
|
||||||
scope: TableHeaderScope,
|
scope: TableHeaderScope,
|
||||||
} in [TableColumnContent] with FlowContent;
|
} in [TableColumnContent] with FlowContent;
|
||||||
thead in [TableContent] with Element_tr;
|
thead in [TableContent] with tr;
|
||||||
tr in [TableContent] with TableColumnContent;
|
tr in [TableContent] with TableColumnContent;
|
||||||
track {
|
track {
|
||||||
default: bool,
|
default: bool,
|
||||||
|
|
|
@ -5,11 +5,13 @@ extern crate enumset;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate strum_macros;
|
extern crate strum_macros;
|
||||||
|
|
||||||
|
pub extern crate htmlescape;
|
||||||
extern crate http;
|
extern crate http;
|
||||||
extern crate language_tags;
|
extern crate language_tags;
|
||||||
extern crate mime;
|
extern crate mime;
|
||||||
extern crate strum;
|
extern crate strum;
|
||||||
extern crate typed_html_macros;
|
extern crate typed_html_macros;
|
||||||
|
|
||||||
|
pub mod dom;
|
||||||
pub mod elements;
|
pub mod elements;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! Types for attribute values.
|
||||||
|
|
||||||
mod class;
|
mod class;
|
||||||
pub use self::class::Class;
|
pub use self::class::Class;
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ extern crate typed_html;
|
||||||
extern crate typed_html_macros;
|
extern crate typed_html_macros;
|
||||||
|
|
||||||
use stdweb::web::{self, IElement, INode};
|
use stdweb::web::{self, IElement, INode};
|
||||||
use typed_html::elements::{Node, VNode};
|
use typed_html::dom::{Node, VNode};
|
||||||
use typed_html_macros::html;
|
use typed_html_macros::html;
|
||||||
|
|
||||||
fn build(
|
fn build(
|
||||||
|
|
Loading…
Reference in New Issue