aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/htmlcollection.rs
diff options
context:
space:
mode:
authorJack Moffitt <jack@metajack.im>2014-08-28 09:34:23 -0600
committerJack Moffitt <jack@metajack.im>2014-09-08 20:21:42 -0600
commitc6ab60dbfc6da7b4f800c9e40893c8b58413960c (patch)
treed1d74076cf7fa20e4f77ec7cb82cae98b67362cb /components/script/dom/htmlcollection.rs
parentdb2f642c32fc5bed445bb6f2e45b0f6f0b4342cf (diff)
downloadservo-c6ab60dbfc6da7b4f800c9e40893c8b58413960c.tar.gz
servo-c6ab60dbfc6da7b4f800c9e40893c8b58413960c.zip
Cargoify servo
Diffstat (limited to 'components/script/dom/htmlcollection.rs')
-rw-r--r--components/script/dom/htmlcollection.rs257
1 files changed, 257 insertions, 0 deletions
diff --git a/components/script/dom/htmlcollection.rs b/components/script/dom/htmlcollection.rs
new file mode 100644
index 00000000000..5bef6c56ff5
--- /dev/null
+++ b/components/script/dom/htmlcollection.rs
@@ -0,0 +1,257 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLCollectionBinding;
+use dom::bindings::codegen::Bindings::HTMLCollectionBinding::HTMLCollectionMethods;
+use dom::bindings::codegen::InheritTypes::{ElementCast, NodeCast};
+use dom::bindings::global::Window;
+use dom::bindings::js::{JS, JSRef, Temporary};
+use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
+use dom::element::{Element, AttributeHandlers, ElementHelpers};
+use dom::node::{Node, NodeHelpers};
+use dom::window::Window;
+use servo_util::atom::Atom;
+use servo_util::namespace;
+use servo_util::namespace::Namespace;
+use servo_util::str::{DOMString, split_html_space_chars};
+
+use serialize::{Encoder, Encodable};
+use std::ascii::StrAsciiExt;
+
+pub trait CollectionFilter {
+ fn filter(&self, elem: &JSRef<Element>, root: &JSRef<Node>) -> bool;
+}
+
+impl<S: Encoder<E>, E> Encodable<S, E> for Box<CollectionFilter> {
+ fn encode(&self, _s: &mut S) -> Result<(), E> {
+ Ok(())
+ }
+}
+
+#[deriving(Encodable)]
+pub enum CollectionTypeId {
+ Static(Vec<JS<Element>>),
+ Live(JS<Node>, Box<CollectionFilter>)
+}
+
+#[deriving(Encodable)]
+pub struct HTMLCollection {
+ collection: CollectionTypeId,
+ reflector_: Reflector,
+}
+
+impl HTMLCollection {
+ pub fn new_inherited(collection: CollectionTypeId) -> HTMLCollection {
+ HTMLCollection {
+ collection: collection,
+ reflector_: Reflector::new(),
+ }
+ }
+
+ pub fn new(window: &JSRef<Window>, collection: CollectionTypeId) -> Temporary<HTMLCollection> {
+ reflect_dom_object(box HTMLCollection::new_inherited(collection),
+ &Window(*window), HTMLCollectionBinding::Wrap)
+ }
+}
+
+impl HTMLCollection {
+ pub fn create(window: &JSRef<Window>, root: &JSRef<Node>,
+ filter: Box<CollectionFilter>) -> Temporary<HTMLCollection> {
+ HTMLCollection::new(window, Live(JS::from_rooted(root), filter))
+ }
+
+ fn all_elements(window: &JSRef<Window>, root: &JSRef<Node>,
+ namespace_filter: Option<Namespace>) -> Temporary<HTMLCollection> {
+ struct AllElementFilter {
+ namespace_filter: Option<Namespace>
+ }
+ impl CollectionFilter for AllElementFilter {
+ fn filter(&self, elem: &JSRef<Element>, _root: &JSRef<Node>) -> bool {
+ match self.namespace_filter {
+ None => true,
+ Some(ref namespace) => elem.namespace == *namespace
+ }
+ }
+ }
+ let filter = AllElementFilter {namespace_filter: namespace_filter};
+ HTMLCollection::create(window, root, box filter)
+ }
+
+ pub fn by_tag_name(window: &JSRef<Window>, root: &JSRef<Node>, tag: DOMString)
+ -> Temporary<HTMLCollection> {
+ if tag.as_slice() == "*" {
+ return HTMLCollection::all_elements(window, root, None);
+ }
+
+ struct TagNameFilter {
+ tag: Atom,
+ ascii_lower_tag: Atom,
+ }
+ impl CollectionFilter for TagNameFilter {
+ fn filter(&self, elem: &JSRef<Element>, _root: &JSRef<Node>) -> bool {
+ if elem.html_element_in_html_document() {
+ elem.local_name == self.ascii_lower_tag
+ } else {
+ elem.local_name == self.tag
+ }
+ }
+ }
+ let filter = TagNameFilter {
+ tag: Atom::from_slice(tag.as_slice()),
+ ascii_lower_tag: Atom::from_slice(tag.as_slice().to_ascii_lower().as_slice()),
+ };
+ HTMLCollection::create(window, root, box filter)
+ }
+
+ pub fn by_tag_name_ns(window: &JSRef<Window>, root: &JSRef<Node>, tag: DOMString,
+ maybe_ns: Option<DOMString>) -> Temporary<HTMLCollection> {
+ let namespace_filter = match maybe_ns {
+ Some(namespace) => {
+ match namespace.as_slice() {
+ "*" => None,
+ ns => Some(Namespace::from_str(ns)),
+ }
+ },
+ None => Some(namespace::Null),
+ };
+
+ if tag.as_slice() == "*" {
+ return HTMLCollection::all_elements(window, root, namespace_filter);
+ }
+ struct TagNameNSFilter {
+ tag: Atom,
+ namespace_filter: Option<Namespace>
+ }
+ impl CollectionFilter for TagNameNSFilter {
+ fn filter(&self, elem: &JSRef<Element>, _root: &JSRef<Node>) -> bool {
+ let ns_match = match self.namespace_filter {
+ Some(ref namespace) => {
+ elem.deref().namespace == *namespace
+ },
+ None => true
+ };
+ ns_match && elem.deref().local_name == self.tag
+ }
+ }
+ let filter = TagNameNSFilter {
+ tag: Atom::from_slice(tag.as_slice()),
+ namespace_filter: namespace_filter
+ };
+ HTMLCollection::create(window, root, box filter)
+ }
+
+ pub fn by_class_name(window: &JSRef<Window>, root: &JSRef<Node>, classes: DOMString)
+ -> Temporary<HTMLCollection> {
+ struct ClassNameFilter {
+ classes: Vec<DOMString>
+ }
+ impl CollectionFilter for ClassNameFilter {
+ fn filter(&self, elem: &JSRef<Element>, _root: &JSRef<Node>) -> bool {
+ self.classes.iter().all(|class| elem.has_class(class.as_slice()))
+ }
+ }
+ let filter = ClassNameFilter {
+ classes: split_html_space_chars(classes.as_slice()).map(|class| class.to_string()).collect()
+ };
+ HTMLCollection::create(window, root, box filter)
+ }
+
+ pub fn children(window: &JSRef<Window>, root: &JSRef<Node>) -> Temporary<HTMLCollection> {
+ struct ElementChildFilter;
+ impl CollectionFilter for ElementChildFilter {
+ fn filter(&self, elem: &JSRef<Element>, root: &JSRef<Node>) -> bool {
+ root.is_parent_of(NodeCast::from_ref(elem))
+ }
+ }
+ HTMLCollection::create(window, root, box ElementChildFilter)
+ }
+}
+
+impl<'a> HTMLCollectionMethods for JSRef<'a, HTMLCollection> {
+ // http://dom.spec.whatwg.org/#dom-htmlcollection-length
+ fn Length(&self) -> u32 {
+ match self.collection {
+ Static(ref elems) => elems.len() as u32,
+ Live(ref root, ref filter) => {
+ let root = root.root();
+ root.deref().traverse_preorder()
+ .filter(|&child| {
+ let elem: Option<&JSRef<Element>> = ElementCast::to_ref(&child);
+ elem.map_or(false, |elem| filter.filter(elem, &*root))
+ }).count() as u32
+ }
+ }
+ }
+
+ // http://dom.spec.whatwg.org/#dom-htmlcollection-item
+ fn Item(&self, index: u32) -> Option<Temporary<Element>> {
+ match self.collection {
+ Static(ref elems) => elems
+ .as_slice()
+ .get(index as uint)
+ .map(|elem| Temporary::new(elem.clone())),
+ Live(ref root, ref filter) => {
+ let root = root.root();
+ root.deref().traverse_preorder()
+ .filter_map(|node| {
+ let elem: Option<&JSRef<Element>> = ElementCast::to_ref(&node);
+ elem.filtered(|&elem| filter.filter(elem, &*root))
+ .map(|elem| elem.clone())
+ })
+ .nth(index as uint)
+ .clone()
+ .map(|elem| Temporary::from_rooted(&elem))
+ }
+ }
+ }
+
+ // http://dom.spec.whatwg.org/#dom-htmlcollection-nameditem
+ fn NamedItem(&self, key: DOMString) -> Option<Temporary<Element>> {
+ // Step 1.
+ if key.is_empty() {
+ return None;
+ }
+
+ // Step 2.
+ match self.collection {
+ Static(ref elems) => elems.iter()
+ .map(|elem| elem.root())
+ .find(|elem| {
+ elem.get_string_attribute("name") == key ||
+ elem.get_string_attribute("id") == key })
+ .map(|maybe_elem| Temporary::from_rooted(&*maybe_elem)),
+ Live(ref root, ref filter) => {
+ let root = root.root();
+ root.deref().traverse_preorder()
+ .filter_map(|node| {
+ let elem: Option<&JSRef<Element>> = ElementCast::to_ref(&node);
+ elem.filtered(|&elem| filter.filter(elem, &*root))
+ .map(|elem| elem.clone())
+ })
+ .find(|elem| {
+ elem.get_string_attribute("name") == key ||
+ elem.get_string_attribute("id") == key })
+ .map(|maybe_elem| Temporary::from_rooted(&maybe_elem))
+ }
+ }
+ }
+
+ fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<Temporary<Element>> {
+ let maybe_elem = self.Item(index);
+ *found = maybe_elem.is_some();
+ maybe_elem
+ }
+
+ fn NamedGetter(&self, name: DOMString, found: &mut bool) -> Option<Temporary<Element>> {
+ let maybe_elem = self.NamedItem(name);
+ *found = maybe_elem.is_some();
+ maybe_elem
+ }
+}
+
+impl Reflectable for HTMLCollection {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ &self.reflector_
+ }
+}