///|
/// | An interface from which a number of DOM API object types inherit.
/// |
/// | [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Node)
#external
pub(all) type Node

///|
/// | Returns a live `NodeList` of child nodes of the given `Node`.
/// |
/// | [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Node/childNodes)
pub extern "js" fn Node::child_nodes(self : Node) -> Array[Node] =
  #| (self) => self.childNodes

// children

///|
/// | Returns a live `HTMLCollection` containing all of the `Element` children of the node upon which it was called.
/// |
/// | [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Element/children)
pub extern "js" fn Node::children(self : Node) -> Array[Node] =
  #| (self) => self.children

///|
/// | Reinterprets a `Node` as an `Element`. This is a type cast and does not change the underlying object.
pub extern "js" fn Node::reinterpret_as_element(self : Node) -> Element =
  #| (self) => self

// reinterpret_as_node

///|
/// | Reinterprets an `Element` as a `Node`. This is a type cast and does not change the underlying object.
pub extern "js" fn Element::reinterpret_as_node(self : Element) -> Node =
  #| (self) => self

// first_child

///|
/// | Internal helper to get the first child as `JsObscure`.
extern "js" fn Node::js_get_first_child(self : Node) -> JsObscure =
  #| (self) => self.firstChild

///|
/// | Returns the node's first child in the tree, or `None` if the node has no children.
/// |
/// | [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Node/firstChild)
pub fn Node::first_child(self : Node) -> Node? {
  let first_child = self.js_get_first_child()
  if first_child.is_nil() {
    None
  } else {
    Some(js_obscure_to_v(first_child))
  }
}

///|
/// | Represents a rectangle describing the size and position of an element.
/// |
/// | [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/DOMRect)
pub(all) struct DomRect {
  /// The x coordinate of the DOMRect's origin.
  x : Float
  /// The y coordinate of the DOMRect's origin.
  y : Float
  /// The width of the DOMRect.
  width : Float
  /// The height of the DOMRect.
  height : Float
  /// The top coordinate value of the DOMRect (same as y).
  top : Float
  /// The right coordinate value of the DOMRect (x + width).
  right : Float
  /// The bottom coordinate value of the DOMRect (y + height).
  bottom : Float
  /// The left coordinate value of the DOMRect (same as x).
  left : Float
}

///|
/// | Internal JS extern to get bounding client rect.
extern "js" fn js_get_bounding_client_rect(el : Element) -> FixedArray[Float] =
  #| (el) => { const r = el.getBoundingClientRect(); return [r.x, r.y, r.width, r.height, r.top, r.right, r.bottom, r.left]; }

///|
/// | Returns the size of an element and its position relative to the viewport.
/// |
/// | [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect)
pub fn Element::get_bounding_client_rect(self : Element) -> DomRect {
  let arr = js_get_bounding_client_rect(self)
  {
    x: arr[0],
    y: arr[1],
    width: arr[2],
    height: arr[3],
    top: arr[4],
    right: arr[5],
    bottom: arr[6],
    left: arr[7],
  }
}

///|
/// | The most general base class from which all objects in a `Document` inherit.
/// |
/// | [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Element)
#external
pub(all) type Element

///|
/// | Sets the HTML markup contained within the element.
/// |
/// | [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML)
pub extern "js" fn Element::set_inner_html(
  self : Element,
  html : String,
) -> Unit =
  #| (self, html) => { self.innerHTML = html }

///|
/// | Sets the text content of a node and its descendants.
/// |
/// | [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent)
pub extern "js" fn Element::set_inner_text(
  self : Element,
  text : String,
) -> Unit =
  #| (self, text) => { self.innerText = text }

///|
/// | Sets the `for` attribute of a label element.
/// |
/// | [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/htmlFor)
pub extern "js" fn Element::set_html_for(self : Element, html : String) -> Unit =
  #| (self, html) => { self.htmlFor = html }

///|
/// | Returns the tag name of the element in upper case.
/// |
/// | [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Element/tagName)
pub extern "js" fn Element::tag_name(self : Element) -> String =
  #| (self) => self.tagName

///|
/// | Sets the value of an attribute on the specified element.
/// |
/// | [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute)
pub extern "js" fn Element::set_attribute(
  self : Element,
  name : String,
  value : String,
) -> Unit =
  #| (self, name, value) => { self.setAttribute(name, value) }

// remove_attribute

///|
/// | Removes an attribute from the specified element.
/// |
/// | [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Element/removeAttribute)
pub extern "js" fn Element::remove_attribute(
  self : Element,
  name : String,
) -> Unit =
  #| (self, name) => { self.removeAttribute(name) }

///|
/// | Sets a custom data attribute (`data-*`).
/// |
/// | [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset)
pub extern "js" fn Element::set_data_attribute(
  self : Element,
  name : String,
  value : String,
) -> Unit =
  #| (self, name, value) => { self.dataset[name] = value }

///|
/// | Gets the value of a custom data attribute (`data-*`).
/// |
/// | [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset)
pub extern "js" fn Element::data_attribute(
  self : Element,
  name : String,
) -> String =
  #| (self, name) => self.dataset[name]

///|
/// | Removes a custom data attribute (`data-*`).
/// |
/// | [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset)
pub extern "js" fn Element::remove_data_attribute(
  self : Element,
  name : String,
) -> Unit =
  #| (self, name) => { delete self.dataset[name] }

///|
/// | Adds a node to the end of the list of children of a specified parent node.
/// |
/// | [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/Node/appendChild)
pub extern "js" fn Element::append_child(self : Element, child : Node) -> Unit =
  #| (self, child) => { self.appendChild(child) }

// set_onkeypress on input

///|
/// | Sets the `onkeypress` event handler for an `` element.
/// |
/// | [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onkeypress)
pub extern "js" fn HtmlInputElement::set_onkeypress(
  self : HtmlInputElement,
  f : ((KeyboardEvent) -> Unit)?,
) -> Unit =
  #| (self, f) => { self.onkeypress = f }

// set_onkeypress on textarea

///|
/// | Sets the `onkeypress` event handler for a `