This commit is contained in:
parent
a96a518d42
commit
72cde36478
2
apps/merge_docs.wren
vendored
2
apps/merge_docs.wren
vendored
@ -23,7 +23,7 @@ all.add(" - websocket library")
|
||||
all.add("")
|
||||
all.add("## Documentation")
|
||||
|
||||
for (path in Path.new("manual").rglob("*.html")) {
|
||||
for (path in Path.new("bin/manual").rglob("*.html")) {
|
||||
all.add("### " + path.name)
|
||||
all.add("```html```")
|
||||
all.add(path.readText())
|
||||
|
||||
116
example/html_dom_demo.wren
vendored
Normal file
116
example/html_dom_demo.wren
vendored
Normal file
@ -0,0 +1,116 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document, Element, NodeList
|
||||
|
||||
System.print("=== HTML DOM Demo ===\n")
|
||||
|
||||
System.print("--- Parsing HTML ---")
|
||||
var html = """
|
||||
<html>
|
||||
<head>
|
||||
<title>My Page</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="main" class="container">
|
||||
<h1>Welcome</h1>
|
||||
<p class="intro">Hello World!</p>
|
||||
<ul>
|
||||
<li>Item 1</li>
|
||||
<li>Item 2</li>
|
||||
<li>Item 3</li>
|
||||
</ul>
|
||||
</div>
|
||||
<footer>
|
||||
<p>© 2024</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
var doc = Document.parse(html)
|
||||
System.print("Document title: %(doc.title)")
|
||||
System.print("Has body: %(doc.body != null)")
|
||||
System.print("Has head: %(doc.head != null)")
|
||||
|
||||
System.print("\n--- Querying Elements ---")
|
||||
var mainDiv = doc.getElementById("main")
|
||||
System.print("Found #main: %(mainDiv.tagName)")
|
||||
System.print("Class name: %(mainDiv.className)")
|
||||
|
||||
var intro = doc.querySelector(".intro")
|
||||
System.print("Intro text: %(intro.textContent)")
|
||||
|
||||
var listItems = doc.querySelectorAll("li")
|
||||
System.print("List items count: %(listItems.count)")
|
||||
for (item in listItems) {
|
||||
System.print(" - %(item.textContent)")
|
||||
}
|
||||
|
||||
System.print("\n--- DOM Traversal ---")
|
||||
var h1 = doc.querySelector("h1")
|
||||
System.print("H1 parent: %(h1.parentElement.tagName)")
|
||||
System.print("H1 next sibling: %(h1.nextElementSibling.tagName)")
|
||||
|
||||
var ul = doc.querySelector("ul")
|
||||
System.print("UL first child: %(ul.firstElementChild.textContent)")
|
||||
System.print("UL last child: %(ul.lastElementChild.textContent)")
|
||||
System.print("UL has children: %(ul.hasChildNodes())")
|
||||
|
||||
System.print("\n--- Creating Elements ---")
|
||||
var newP = doc.createElement("p")
|
||||
newP.textContent = "New paragraph"
|
||||
newP.className = "dynamic"
|
||||
newP.setAttribute("data-index", "1")
|
||||
|
||||
mainDiv.appendChild(newP)
|
||||
System.print("Added new paragraph to main div")
|
||||
System.print("Main div children: %(mainDiv.children.count)")
|
||||
|
||||
System.print("\n--- Modifying Content ---")
|
||||
var footer = doc.querySelector("footer p")
|
||||
System.print("Footer original: %(footer.textContent)")
|
||||
footer.textContent = "Updated footer"
|
||||
System.print("Footer updated: %(footer.textContent)")
|
||||
|
||||
System.print("\n--- Working with Attributes ---")
|
||||
var div = doc.getElementById("main")
|
||||
System.print("ID: %(div.id)")
|
||||
System.print("Has class attr: %(div.hasAttribute("class"))")
|
||||
|
||||
var classList = div.classList
|
||||
System.print("Classes: %(classList.join(", "))")
|
||||
|
||||
div.setAttribute("role", "main")
|
||||
System.print("New role attr: %(div.getAttribute("role"))")
|
||||
|
||||
System.print("\n--- Cloning Nodes ---")
|
||||
var clone = intro.cloneNode(true)
|
||||
System.print("Cloned text: %(clone.textContent)")
|
||||
System.print("Clone has parent: %(clone.parentNode != null)")
|
||||
|
||||
System.print("\n--- Selector Matching ---")
|
||||
System.print("H1 matches 'h1': %(h1.matches("h1"))")
|
||||
System.print("H1 matches '.intro': %(h1.matches(".intro"))")
|
||||
|
||||
var closest = intro.closest("div")
|
||||
System.print("Intro closest div: %(closest.id)")
|
||||
|
||||
System.print("\n--- Serialization ---")
|
||||
var smallHtml = "<div><p>Test</p></div>"
|
||||
var doc2 = Document.parse(smallHtml)
|
||||
System.print("Serialized: %(doc2.outerHTML)")
|
||||
|
||||
System.print("\n--- Complex Selectors ---")
|
||||
var byTag = doc.getElementsByTagName("p")
|
||||
System.print("All p elements: %(byTag.count)")
|
||||
|
||||
var byClass = doc.getElementsByClassName("intro")
|
||||
System.print("Elements with .intro: %(byClass.count)")
|
||||
|
||||
var descendant = doc.querySelector("div p")
|
||||
System.print("div p found: %(descendant != null)")
|
||||
|
||||
var child = doc.querySelector("div > h1")
|
||||
System.print("div > h1 found: %(child != null)")
|
||||
|
||||
System.print("\n=== Demo Complete ===")
|
||||
@ -9,9 +9,9 @@
|
||||
{% block article %}
|
||||
<h1>html</h1>
|
||||
|
||||
<p>The <code>html</code> module provides utilities for HTML and URL encoding/decoding, slug generation, and query string handling.</p>
|
||||
<p>The <code>html</code> module provides HTML/URL utilities and a complete DOM implementation for parsing, querying, manipulating, and serializing HTML documents.</p>
|
||||
|
||||
<pre><code>import "html" for Html</code></pre>
|
||||
<pre><code>import "html" for Html, Document, Element, TextNode, NodeList</code></pre>
|
||||
|
||||
<h2>Html Class</h2>
|
||||
|
||||
@ -23,163 +23,899 @@
|
||||
<h3>Static Methods</h3>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">Html.urlencode</span>(<span class="param">string</span>) → <span class="type">String</span>
|
||||
<span class="method-name">Html.urlencode</span>(<span class="param">string</span>) → <span class="type">String</span>
|
||||
</div>
|
||||
<p>URL-encodes a string for use in URLs and query parameters. Spaces become <code>+</code>, special characters become percent-encoded.</p>
|
||||
<p>URL-encodes a string for use in URLs and query parameters.</p>
|
||||
<ul class="param-list">
|
||||
<li><span class="param-name">string</span> <span class="param-type">(String)</span> - The string to encode</li>
|
||||
<li><span class="returns">Returns:</span> URL-encoded string</li>
|
||||
</ul>
|
||||
<pre><code>System.print(Html.urlencode("hello world")) // hello+world
|
||||
System.print(Html.urlencode("a=b&c=d")) // a\%3Db\%26c\%3Dd
|
||||
System.print(Html.urlencode("café")) // caf\%C3\%A9</code></pre>
|
||||
<pre><code>System.print(Html.urlencode("hello world")) // hello+world</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">Html.urldecode</span>(<span class="param">string</span>) → <span class="type">String</span>
|
||||
<span class="method-name">Html.urldecode</span>(<span class="param">string</span>) → <span class="type">String</span>
|
||||
</div>
|
||||
<p>Decodes a URL-encoded string. Converts <code>+</code> to space and decodes percent-encoded characters.</p>
|
||||
<p>Decodes a URL-encoded string.</p>
|
||||
<ul class="param-list">
|
||||
<li><span class="param-name">string</span> <span class="param-type">(String)</span> - The URL-encoded string</li>
|
||||
<li><span class="returns">Returns:</span> Decoded string</li>
|
||||
</ul>
|
||||
<pre><code>System.print(Html.urldecode("hello+world")) // hello world
|
||||
System.print(Html.urldecode("caf\%C3\%A9")) // café</code></pre>
|
||||
<pre><code>System.print(Html.urldecode("hello+world")) // hello world</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">Html.slugify</span>(<span class="param">string</span>) → <span class="type">String</span>
|
||||
<span class="method-name">Html.slugify</span>(<span class="param">string</span>) → <span class="type">String</span>
|
||||
</div>
|
||||
<p>Converts a string to a URL-friendly slug. Lowercase, alphanumeric characters with hyphens.</p>
|
||||
<p>Converts a string to a URL-friendly slug.</p>
|
||||
<ul class="param-list">
|
||||
<li><span class="param-name">string</span> <span class="param-type">(String)</span> - The string to slugify</li>
|
||||
<li><span class="returns">Returns:</span> URL-friendly slug</li>
|
||||
</ul>
|
||||
<pre><code>System.print(Html.slugify("Hello World")) // hello-world
|
||||
System.print(Html.slugify("My Blog Post!")) // my-blog-post
|
||||
System.print(Html.slugify(" Multiple Spaces ")) // multiple-spaces</code></pre>
|
||||
<pre><code>System.print(Html.slugify("Hello World!")) // hello-world</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">Html.quote</span>(<span class="param">string</span>) → <span class="type">String</span>
|
||||
<span class="method-name">Html.quote</span>(<span class="param">string</span>) → <span class="type">String</span>
|
||||
</div>
|
||||
<p>Escapes HTML special characters to prevent XSS attacks.</p>
|
||||
<ul class="param-list">
|
||||
<li><span class="param-name">string</span> <span class="param-type">(String)</span> - The string to escape</li>
|
||||
<li><span class="returns">Returns:</span> HTML-escaped string</li>
|
||||
</ul>
|
||||
<pre><code>System.print(Html.quote("<script>alert('xss')</script>"))
|
||||
// &lt;script&gt;alert(&#39;xss&#39;)&lt;/script&gt;
|
||||
|
||||
System.print(Html.quote("A & B")) // A &amp; B
|
||||
System.print(Html.quote("\"quoted\"")) // &quot;quoted&quot;</code></pre>
|
||||
<pre><code>System.print(Html.quote("<script>")) // &lt;script&gt;</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">Html.unquote</span>(<span class="param">string</span>) → <span class="type">String</span>
|
||||
<span class="method-name">Html.unquote</span>(<span class="param">string</span>) → <span class="type">String</span>
|
||||
</div>
|
||||
<p>Unescapes HTML entities back to their original characters.</p>
|
||||
<ul class="param-list">
|
||||
<li><span class="param-name">string</span> <span class="param-type">(String)</span> - The HTML-escaped string</li>
|
||||
<li><span class="returns">Returns:</span> Unescaped string</li>
|
||||
</ul>
|
||||
<pre><code>System.print(Html.unquote("&lt;div&gt;")) // <div>
|
||||
System.print(Html.unquote("A &amp; B")) // A & B</code></pre>
|
||||
<pre><code>System.print(Html.unquote("&lt;div&gt;")) // <div></code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">Html.encodeParams</span>(<span class="param">params</span>) → <span class="type">String</span>
|
||||
<span class="method-name">Html.encodeParams</span>(<span class="param">params</span>) → <span class="type">String</span>
|
||||
</div>
|
||||
<p>Encodes a map of key-value pairs into a URL query string.</p>
|
||||
<ul class="param-list">
|
||||
<li><span class="param-name">params</span> <span class="param-type">(Map)</span> - Map of parameters to encode</li>
|
||||
<li><span class="returns">Returns:</span> URL-encoded query string</li>
|
||||
</ul>
|
||||
<pre><code>var params = {"name": "John Doe", "age": 30}
|
||||
System.print(Html.encodeParams(params)) // name=John+Doe&age=30
|
||||
|
||||
var search = {"q": "wren lang", "page": 1}
|
||||
System.print(Html.encodeParams(search)) // q=wren+lang&page=1</code></pre>
|
||||
<pre><code>System.print(Html.encodeParams({"name": "John", "age": 30})) // name=John&age=30</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">Html.decodeParams</span>(<span class="param">string</span>) → <span class="type">Map</span>
|
||||
<span class="method-name">Html.decodeParams</span>(<span class="param">string</span>) → <span class="type">Map</span>
|
||||
</div>
|
||||
<p>Decodes a URL query string into a map of key-value pairs.</p>
|
||||
<ul class="param-list">
|
||||
<li><span class="param-name">string</span> <span class="param-type">(String)</span> - URL-encoded query string</li>
|
||||
<li><span class="returns">Returns:</span> Map of decoded parameters</li>
|
||||
</ul>
|
||||
<pre><code>var params = Html.decodeParams("name=John+Doe&age=30")
|
||||
System.print(params["name"]) // John Doe
|
||||
System.print(params["age"]) // 30</code></pre>
|
||||
<pre><code>var params = Html.decodeParams("name=John&age=30")
|
||||
System.print(params["name"]) // John</code></pre>
|
||||
|
||||
<h2>Entity Reference</h2>
|
||||
<hr>
|
||||
|
||||
<h2>Document Class</h2>
|
||||
|
||||
<div class="class-header">
|
||||
<h3>Document</h3>
|
||||
<p>Represents a parsed HTML document. Provides methods to query and manipulate the DOM tree.</p>
|
||||
</div>
|
||||
|
||||
<h3>Constructor</h3>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">Document.parse</span>(<span class="param">html</span>) → <span class="type">Document</span>
|
||||
</div>
|
||||
<p>Parses an HTML string and returns a Document object.</p>
|
||||
<ul class="param-list">
|
||||
<li><span class="param-name">html</span> <span class="param-type">(String)</span> - HTML string to parse</li>
|
||||
<li><span class="returns">Returns:</span> Document object representing the parsed HTML</li>
|
||||
</ul>
|
||||
<pre><code>var doc = Document.parse("<div><p>Hello</p></div>")
|
||||
System.print(doc.body) // Element</code></pre>
|
||||
|
||||
<h3>Properties</h3>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">document.documentElement</span> → <span class="type">Element</span>
|
||||
</div>
|
||||
<p>Returns the root element of the document (typically the html element).</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">document.head</span> → <span class="type">Element|Null</span>
|
||||
</div>
|
||||
<p>Returns the head element of the document, or null if not present.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">document.body</span> → <span class="type">Element|Null</span>
|
||||
</div>
|
||||
<p>Returns the body element of the document, or null if not present.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">document.title</span> → <span class="type">String</span>
|
||||
</div>
|
||||
<p>Gets the document title (content of the title element).</p>
|
||||
<pre><code>var doc = Document.parse("<title>My Page</title>")
|
||||
System.print(doc.title) // My Page</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">document.title=</span>(<span class="param">value</span>)
|
||||
</div>
|
||||
<p>Sets the document title.</p>
|
||||
<pre><code>doc.title = "New Title"</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">document.innerHTML</span> → <span class="type">String</span>
|
||||
</div>
|
||||
<p>Returns the inner HTML content of the document element.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">document.outerHTML</span> → <span class="type">String</span>
|
||||
</div>
|
||||
<p>Returns the complete HTML serialization of the document.</p>
|
||||
<pre><code>var doc = Document.parse("<div>Hello</div>")
|
||||
System.print(doc.outerHTML) // <html><div>Hello</div></html></code></pre>
|
||||
|
||||
<h3>Query Methods</h3>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">document.querySelector</span>(<span class="param">selector</span>) → <span class="type">Element|Null</span>
|
||||
</div>
|
||||
<p>Returns the first element matching the CSS selector, or null if not found.</p>
|
||||
<ul class="param-list">
|
||||
<li><span class="param-name">selector</span> <span class="param-type">(String)</span> - CSS selector string</li>
|
||||
<li><span class="returns">Returns:</span> First matching Element or null</li>
|
||||
</ul>
|
||||
<pre><code>var doc = Document.parse("<div class='box'>Content</div>")
|
||||
var elem = doc.querySelector(".box")
|
||||
System.print(elem.textContent) // Content</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">document.querySelectorAll</span>(<span class="param">selector</span>) → <span class="type">NodeList</span>
|
||||
</div>
|
||||
<p>Returns all elements matching the CSS selector.</p>
|
||||
<ul class="param-list">
|
||||
<li><span class="param-name">selector</span> <span class="param-type">(String)</span> - CSS selector string</li>
|
||||
<li><span class="returns">Returns:</span> NodeList of matching elements</li>
|
||||
</ul>
|
||||
<pre><code>var doc = Document.parse("<p>One</p><p>Two</p>")
|
||||
var elems = doc.querySelectorAll("p")
|
||||
System.print(elems.count) // 2</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">document.getElementById</span>(<span class="param">id</span>) → <span class="type">Element|Null</span>
|
||||
</div>
|
||||
<p>Returns the element with the specified ID, or null if not found.</p>
|
||||
<ul class="param-list">
|
||||
<li><span class="param-name">id</span> <span class="param-type">(String)</span> - The element ID to search for</li>
|
||||
<li><span class="returns">Returns:</span> Element with matching ID or null</li>
|
||||
</ul>
|
||||
<pre><code>var doc = Document.parse("<div id='main'>Content</div>")
|
||||
var elem = doc.getElementById("main")
|
||||
System.print(elem.tagName) // DIV</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">document.getElementsByClassName</span>(<span class="param">className</span>) → <span class="type">NodeList</span>
|
||||
</div>
|
||||
<p>Returns all elements with the specified class name.</p>
|
||||
<ul class="param-list">
|
||||
<li><span class="param-name">className</span> <span class="param-type">(String)</span> - The class name to search for</li>
|
||||
<li><span class="returns">Returns:</span> NodeList of matching elements</li>
|
||||
</ul>
|
||||
<pre><code>var doc = Document.parse("<div class='item'>A</div><div class='item'>B</div>")
|
||||
var elems = doc.getElementsByClassName("item")
|
||||
System.print(elems.count) // 2</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">document.getElementsByTagName</span>(<span class="param">tagName</span>) → <span class="type">NodeList</span>
|
||||
</div>
|
||||
<p>Returns all elements with the specified tag name.</p>
|
||||
<ul class="param-list">
|
||||
<li><span class="param-name">tagName</span> <span class="param-type">(String)</span> - The tag name to search for (case-insensitive)</li>
|
||||
<li><span class="returns">Returns:</span> NodeList of matching elements</li>
|
||||
</ul>
|
||||
<pre><code>var doc = Document.parse("<p>One</p><p>Two</p><span>Three</span>")
|
||||
var elems = doc.getElementsByTagName("p")
|
||||
System.print(elems.count) // 2</code></pre>
|
||||
|
||||
<h3>Element Creation</h3>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">document.createElement</span>(<span class="param">tagName</span>) → <span class="type">Element</span>
|
||||
</div>
|
||||
<p>Creates a new element with the specified tag name.</p>
|
||||
<ul class="param-list">
|
||||
<li><span class="param-name">tagName</span> <span class="param-type">(String)</span> - The tag name for the new element</li>
|
||||
<li><span class="returns">Returns:</span> New Element object</li>
|
||||
</ul>
|
||||
<pre><code>var doc = Document.parse("<div></div>")
|
||||
var p = doc.createElement("p")
|
||||
p.textContent = "Hello"
|
||||
doc.querySelector("div").appendChild(p)</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">document.createTextNode</span>(<span class="param">text</span>) → <span class="type">TextNode</span>
|
||||
</div>
|
||||
<p>Creates a new text node with the specified content.</p>
|
||||
<ul class="param-list">
|
||||
<li><span class="param-name">text</span> <span class="param-type">(String)</span> - The text content</li>
|
||||
<li><span class="returns">Returns:</span> New TextNode object</li>
|
||||
</ul>
|
||||
<pre><code>var doc = Document.parse("<div></div>")
|
||||
var text = doc.createTextNode("Hello World")
|
||||
doc.querySelector("div").appendChild(text)</code></pre>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2>Element Class</h2>
|
||||
|
||||
<div class="class-header">
|
||||
<h3>Element</h3>
|
||||
<p>Represents an HTML element in the DOM tree. Provides methods for traversal, manipulation, and querying.</p>
|
||||
</div>
|
||||
|
||||
<h3>Identity Properties</h3>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.tagName</span> → <span class="type">String</span>
|
||||
</div>
|
||||
<p>Returns the tag name of the element in uppercase.</p>
|
||||
<pre><code>var div = doc.querySelector("div")
|
||||
System.print(div.tagName) // DIV</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.id</span> → <span class="type">String</span>
|
||||
</div>
|
||||
<p>Gets the ID attribute of the element.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.id=</span>(<span class="param">value</span>)
|
||||
</div>
|
||||
<p>Sets the ID attribute of the element.</p>
|
||||
<pre><code>elem.id = "newId"
|
||||
System.print(elem.id) // newId</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.className</span> → <span class="type">String</span>
|
||||
</div>
|
||||
<p>Gets the class attribute as a string.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.className=</span>(<span class="param">value</span>)
|
||||
</div>
|
||||
<p>Sets the class attribute.</p>
|
||||
<pre><code>elem.className = "foo bar"
|
||||
System.print(elem.className) // foo bar</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.classList</span> → <span class="type">List</span>
|
||||
</div>
|
||||
<p>Returns the class names as a list of strings.</p>
|
||||
<pre><code>elem.className = "foo bar baz"
|
||||
var classes = elem.classList
|
||||
System.print(classes[0]) // foo
|
||||
System.print(classes.count) // 3</code></pre>
|
||||
|
||||
<h3>Node Properties</h3>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.nodeType</span> → <span class="type">Num</span>
|
||||
</div>
|
||||
<p>Returns the node type (1 for Element).</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.nodeName</span> → <span class="type">String</span>
|
||||
</div>
|
||||
<p>Returns the node name (same as tagName for elements).</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.nodeValue</span> → <span class="type">Null</span>
|
||||
</div>
|
||||
<p>Returns null for elements (nodeValue is only meaningful for text nodes).</p>
|
||||
|
||||
<h3>Content Properties</h3>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.textContent</span> → <span class="type">String</span>
|
||||
</div>
|
||||
<p>Gets the text content of the element and all descendants.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.textContent=</span>(<span class="param">value</span>)
|
||||
</div>
|
||||
<p>Sets the text content, replacing all children with a single text node.</p>
|
||||
<pre><code>elem.textContent = "New text"</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.innerHTML</span> → <span class="type">String</span>
|
||||
</div>
|
||||
<p>Gets the HTML content inside the element.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.innerHTML=</span>(<span class="param">html</span>)
|
||||
</div>
|
||||
<p>Sets the inner HTML, parsing and replacing all children.</p>
|
||||
<pre><code>elem.innerHTML = "<p>New content</p>"</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.outerHTML</span> → <span class="type">String</span>
|
||||
</div>
|
||||
<p>Gets the HTML serialization of the element including itself.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.outerHTML=</span>(<span class="param">html</span>)
|
||||
</div>
|
||||
<p>Replaces the element with the parsed HTML.</p>
|
||||
|
||||
<h3>Attribute Methods</h3>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.getAttribute</span>(<span class="param">name</span>) → <span class="type">String|Null</span>
|
||||
</div>
|
||||
<p>Gets the value of the specified attribute, or null if not present.</p>
|
||||
<ul class="param-list">
|
||||
<li><span class="param-name">name</span> <span class="param-type">(String)</span> - Attribute name</li>
|
||||
<li><span class="returns">Returns:</span> Attribute value or null</li>
|
||||
</ul>
|
||||
<pre><code>var href = elem.getAttribute("href")
|
||||
System.print(href)</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.setAttribute</span>(<span class="param">name</span>, <span class="param">value</span>)
|
||||
</div>
|
||||
<p>Sets the value of the specified attribute.</p>
|
||||
<ul class="param-list">
|
||||
<li><span class="param-name">name</span> <span class="param-type">(String)</span> - Attribute name</li>
|
||||
<li><span class="param-name">value</span> <span class="param-type">(String)</span> - Attribute value</li>
|
||||
</ul>
|
||||
<pre><code>elem.setAttribute("data-id", "123")</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.removeAttribute</span>(<span class="param">name</span>)
|
||||
</div>
|
||||
<p>Removes the specified attribute.</p>
|
||||
<ul class="param-list">
|
||||
<li><span class="param-name">name</span> <span class="param-type">(String)</span> - Attribute name to remove</li>
|
||||
</ul>
|
||||
<pre><code>elem.removeAttribute("disabled")</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.hasAttribute</span>(<span class="param">name</span>) → <span class="type">Bool</span>
|
||||
</div>
|
||||
<p>Returns true if the element has the specified attribute.</p>
|
||||
<ul class="param-list">
|
||||
<li><span class="param-name">name</span> <span class="param-type">(String)</span> - Attribute name</li>
|
||||
<li><span class="returns">Returns:</span> Boolean indicating presence</li>
|
||||
</ul>
|
||||
<pre><code>if (elem.hasAttribute("disabled")) {
|
||||
System.print("Element is disabled")
|
||||
}</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.attributes</span> → <span class="type">Map</span>
|
||||
</div>
|
||||
<p>Returns a map of all attributes (name to value).</p>
|
||||
<pre><code>var attrs = elem.attributes
|
||||
for (name in attrs.keys) {
|
||||
System.print("%(name)=%(attrs[name])")
|
||||
}</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.dataset</span> → <span class="type">Map</span>
|
||||
</div>
|
||||
<p>Returns a map of all data-* attributes with camelCase keys.</p>
|
||||
<pre><code>// <div data-user-id="123" data-active="true">
|
||||
var data = elem.dataset
|
||||
System.print(data["userId"]) // 123
|
||||
System.print(data["active"]) // true</code></pre>
|
||||
|
||||
<h3>Traversal Properties</h3>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.parentNode</span> → <span class="type">Element|Null</span>
|
||||
</div>
|
||||
<p>Returns the parent node (element or document).</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.parentElement</span> → <span class="type">Element|Null</span>
|
||||
</div>
|
||||
<p>Returns the parent element, or null if parent is not an element.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.children</span> → <span class="type">NodeList</span>
|
||||
</div>
|
||||
<p>Returns all child elements (excludes text nodes).</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.childNodes</span> → <span class="type">NodeList</span>
|
||||
</div>
|
||||
<p>Returns all child nodes including text nodes.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.firstChild</span> → <span class="type">Element|TextNode|Null</span>
|
||||
</div>
|
||||
<p>Returns the first child node.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.lastChild</span> → <span class="type">Element|TextNode|Null</span>
|
||||
</div>
|
||||
<p>Returns the last child node.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.firstElementChild</span> → <span class="type">Element|Null</span>
|
||||
</div>
|
||||
<p>Returns the first child that is an element.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.lastElementChild</span> → <span class="type">Element|Null</span>
|
||||
</div>
|
||||
<p>Returns the last child that is an element.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.nextSibling</span> → <span class="type">Element|TextNode|Null</span>
|
||||
</div>
|
||||
<p>Returns the next sibling node.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.previousSibling</span> → <span class="type">Element|TextNode|Null</span>
|
||||
</div>
|
||||
<p>Returns the previous sibling node.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.nextElementSibling</span> → <span class="type">Element|Null</span>
|
||||
</div>
|
||||
<p>Returns the next sibling that is an element.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.previousElementSibling</span> → <span class="type">Element|Null</span>
|
||||
</div>
|
||||
<p>Returns the previous sibling that is an element.</p>
|
||||
|
||||
<h3>Manipulation Methods</h3>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.appendChild</span>(<span class="param">child</span>) → <span class="type">Element|TextNode</span>
|
||||
</div>
|
||||
<p>Appends a child node to the end of the element's children.</p>
|
||||
<ul class="param-list">
|
||||
<li><span class="param-name">child</span> <span class="param-type">(Element|TextNode)</span> - Node to append</li>
|
||||
<li><span class="returns">Returns:</span> The appended node</li>
|
||||
</ul>
|
||||
<pre><code>var p = doc.createElement("p")
|
||||
p.textContent = "New paragraph"
|
||||
container.appendChild(p)</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.insertBefore</span>(<span class="param">newNode</span>, <span class="param">refNode</span>) → <span class="type">Element|TextNode</span>
|
||||
</div>
|
||||
<p>Inserts a node before the reference node.</p>
|
||||
<ul class="param-list">
|
||||
<li><span class="param-name">newNode</span> <span class="param-type">(Element|TextNode)</span> - Node to insert</li>
|
||||
<li><span class="param-name">refNode</span> <span class="param-type">(Element|TextNode|Null)</span> - Reference node (null appends)</li>
|
||||
<li><span class="returns">Returns:</span> The inserted node</li>
|
||||
</ul>
|
||||
<pre><code>var newP = doc.createElement("p")
|
||||
container.insertBefore(newP, existingP)</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.removeChild</span>(<span class="param">child</span>) → <span class="type">Element|TextNode</span>
|
||||
</div>
|
||||
<p>Removes a child node from the element.</p>
|
||||
<ul class="param-list">
|
||||
<li><span class="param-name">child</span> <span class="param-type">(Element|TextNode)</span> - Node to remove</li>
|
||||
<li><span class="returns">Returns:</span> The removed node</li>
|
||||
</ul>
|
||||
<pre><code>var removed = container.removeChild(child)
|
||||
System.print(removed.textContent)</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.replaceChild</span>(<span class="param">newChild</span>, <span class="param">oldChild</span>) → <span class="type">Element|TextNode</span>
|
||||
</div>
|
||||
<p>Replaces a child node with a new node.</p>
|
||||
<ul class="param-list">
|
||||
<li><span class="param-name">newChild</span> <span class="param-type">(Element|TextNode)</span> - Replacement node</li>
|
||||
<li><span class="param-name">oldChild</span> <span class="param-type">(Element|TextNode)</span> - Node to replace</li>
|
||||
<li><span class="returns">Returns:</span> The replaced (old) node</li>
|
||||
</ul>
|
||||
<pre><code>var newP = doc.createElement("p")
|
||||
container.replaceChild(newP, oldP)</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.cloneNode</span>(<span class="param">deep</span>) → <span class="type">Element</span>
|
||||
</div>
|
||||
<p>Creates a copy of the element.</p>
|
||||
<ul class="param-list">
|
||||
<li><span class="param-name">deep</span> <span class="param-type">(Bool)</span> - If true, clones all descendants</li>
|
||||
<li><span class="returns">Returns:</span> Cloned element</li>
|
||||
</ul>
|
||||
<pre><code>var shallow = elem.cloneNode(false) // Just the element
|
||||
var deep = elem.cloneNode(true) // Element and all children</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.remove</span>()
|
||||
</div>
|
||||
<p>Removes the element from its parent.</p>
|
||||
<pre><code>elem.remove() // Element is now detached</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.normalize</span>()
|
||||
</div>
|
||||
<p>Merges adjacent text nodes and removes empty text nodes.</p>
|
||||
|
||||
<h3>Query Methods</h3>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.querySelector</span>(<span class="param">selector</span>) → <span class="type">Element|Null</span>
|
||||
</div>
|
||||
<p>Returns the first descendant matching the CSS selector.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.querySelectorAll</span>(<span class="param">selector</span>) → <span class="type">NodeList</span>
|
||||
</div>
|
||||
<p>Returns all descendants matching the CSS selector.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.getElementsByClassName</span>(<span class="param">className</span>) → <span class="type">NodeList</span>
|
||||
</div>
|
||||
<p>Returns all descendants with the specified class.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.getElementsByTagName</span>(<span class="param">tagName</span>) → <span class="type">NodeList</span>
|
||||
</div>
|
||||
<p>Returns all descendants with the specified tag.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.matches</span>(<span class="param">selector</span>) → <span class="type">Bool</span>
|
||||
</div>
|
||||
<p>Returns true if the element matches the CSS selector.</p>
|
||||
<pre><code>if (elem.matches(".active")) {
|
||||
System.print("Element is active")
|
||||
}</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.closest</span>(<span class="param">selector</span>) → <span class="type">Element|Null</span>
|
||||
</div>
|
||||
<p>Returns the closest ancestor (or self) matching the selector.</p>
|
||||
<pre><code>var container = elem.closest(".container")
|
||||
if (container) {
|
||||
System.print("Found container: %(container.id)")
|
||||
}</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.contains</span>(<span class="param">node</span>) → <span class="type">Bool</span>
|
||||
</div>
|
||||
<p>Returns true if the node is a descendant of this element.</p>
|
||||
<pre><code>if (container.contains(child)) {
|
||||
System.print("Child is inside container")
|
||||
}</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">element.hasChildNodes</span>() → <span class="type">Bool</span>
|
||||
</div>
|
||||
<p>Returns true if the element has any child nodes.</p>
|
||||
<pre><code>if (elem.hasChildNodes()) {
|
||||
System.print("Element has children")
|
||||
}</code></pre>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2>TextNode Class</h2>
|
||||
|
||||
<div class="class-header">
|
||||
<h3>TextNode</h3>
|
||||
<p>Represents a text node in the DOM tree.</p>
|
||||
</div>
|
||||
|
||||
<h3>Properties</h3>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">textNode.textContent</span> → <span class="type">String</span>
|
||||
</div>
|
||||
<p>Gets the text content.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">textNode.textContent=</span>(<span class="param">value</span>)
|
||||
</div>
|
||||
<p>Sets the text content.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">textNode.nodeType</span> → <span class="type">Num</span>
|
||||
</div>
|
||||
<p>Returns 3 (TEXT_NODE).</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">textNode.nodeName</span> → <span class="type">String</span>
|
||||
</div>
|
||||
<p>Returns "#text".</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">textNode.nodeValue</span> → <span class="type">String</span>
|
||||
</div>
|
||||
<p>Gets the text content (same as textContent).</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">textNode.nodeValue=</span>(<span class="param">value</span>)
|
||||
</div>
|
||||
<p>Sets the text content.</p>
|
||||
|
||||
<h3>Traversal Properties</h3>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">textNode.parentNode</span> → <span class="type">Element|Null</span>
|
||||
</div>
|
||||
<p>Returns the parent node.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">textNode.parentElement</span> → <span class="type">Element|Null</span>
|
||||
</div>
|
||||
<p>Returns the parent element.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">textNode.nextSibling</span> → <span class="type">Element|TextNode|Null</span>
|
||||
</div>
|
||||
<p>Returns the next sibling node.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">textNode.previousSibling</span> → <span class="type">Element|TextNode|Null</span>
|
||||
</div>
|
||||
<p>Returns the previous sibling node.</p>
|
||||
|
||||
<h3>Methods</h3>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">textNode.cloneNode</span>(<span class="param">deep</span>) → <span class="type">TextNode</span>
|
||||
</div>
|
||||
<p>Creates a copy of the text node (deep parameter is ignored).</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">textNode.remove</span>()
|
||||
</div>
|
||||
<p>Removes the text node from its parent.</p>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2>NodeList Class</h2>
|
||||
|
||||
<div class="class-header">
|
||||
<h3>NodeList</h3>
|
||||
<p>An iterable collection of DOM nodes returned by query methods.</p>
|
||||
</div>
|
||||
|
||||
<h3>Properties</h3>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">nodeList.count</span> → <span class="type">Num</span>
|
||||
</div>
|
||||
<p>Returns the number of nodes in the list.</p>
|
||||
|
||||
<h3>Access Methods</h3>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">nodeList[index]</span> → <span class="type">Element|TextNode|Null</span>
|
||||
</div>
|
||||
<p>Returns the node at the specified index, or null if out of bounds.</p>
|
||||
<pre><code>var first = list[0]
|
||||
var last = list[list.count - 1]</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">nodeList.item</span>(<span class="param">index</span>) → <span class="type">Element|TextNode|Null</span>
|
||||
</div>
|
||||
<p>Same as subscript access.</p>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">nodeList.toList</span> → <span class="type">List</span>
|
||||
</div>
|
||||
<p>Converts the NodeList to a standard Wren List.</p>
|
||||
<pre><code>var list = nodeList.toList
|
||||
for (elem in list) {
|
||||
System.print(elem.tagName)
|
||||
}</code></pre>
|
||||
|
||||
<h3>Iteration</h3>
|
||||
|
||||
<p>NodeList supports iteration with for-in loops:</p>
|
||||
<pre><code>for (elem in doc.querySelectorAll("p")) {
|
||||
System.print(elem.textContent)
|
||||
}</code></pre>
|
||||
|
||||
<div class="method-signature">
|
||||
<span class="method-name">nodeList.forEach</span>(<span class="param">fn</span>)
|
||||
</div>
|
||||
<p>Calls the function for each node in the list.</p>
|
||||
<pre><code>nodeList.forEach {|elem|
|
||||
System.print(elem.tagName)
|
||||
}</code></pre>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2>CSS Selectors</h2>
|
||||
|
||||
<p>The DOM implementation supports the following CSS selector syntax:</p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Character</th>
|
||||
<th>Entity</th>
|
||||
<th>Selector</th>
|
||||
<th>Example</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>&</td>
|
||||
<td>&amp;</td>
|
||||
<td>Tag</td>
|
||||
<td><code>div</code>, <code>p</code></td>
|
||||
<td>Matches elements by tag name</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><</td>
|
||||
<td>&lt;</td>
|
||||
<td>Universal</td>
|
||||
<td><code>*</code></td>
|
||||
<td>Matches all elements</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>></td>
|
||||
<td>&gt;</td>
|
||||
<td>ID</td>
|
||||
<td><code>#myId</code></td>
|
||||
<td>Matches element with id="myId"</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>"</td>
|
||||
<td>&quot;</td>
|
||||
<td>Class</td>
|
||||
<td><code>.myClass</code></td>
|
||||
<td>Matches elements with class="myClass"</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>'</td>
|
||||
<td>&#39;</td>
|
||||
<td>Attribute</td>
|
||||
<td><code>[href]</code></td>
|
||||
<td>Matches elements with href attribute</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Attribute equals</td>
|
||||
<td><code>[type="text"]</code></td>
|
||||
<td>Matches elements with type="text"</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Attribute contains word</td>
|
||||
<td><code>[class~="item"]</code></td>
|
||||
<td>Matches if class contains "item" as a word</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Attribute starts with</td>
|
||||
<td><code>[href^="https"]</code></td>
|
||||
<td>Matches if href starts with "https"</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Attribute ends with</td>
|
||||
<td><code>[src$=".png"]</code></td>
|
||||
<td>Matches if src ends with ".png"</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Attribute contains</td>
|
||||
<td><code>[title*="hello"]</code></td>
|
||||
<td>Matches if title contains "hello"</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Descendant</td>
|
||||
<td><code>div p</code></td>
|
||||
<td>Matches p anywhere inside div</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Child</td>
|
||||
<td><code>div > p</code></td>
|
||||
<td>Matches p that is direct child of div</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Adjacent sibling</td>
|
||||
<td><code>h1 + p</code></td>
|
||||
<td>Matches p immediately after h1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>General sibling</td>
|
||||
<td><code>h1 ~ p</code></td>
|
||||
<td>Matches any p after h1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>:first-child</td>
|
||||
<td><code>p:first-child</code></td>
|
||||
<td>Matches first child element</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>:last-child</td>
|
||||
<td><code>p:last-child</code></td>
|
||||
<td>Matches last child element</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>:nth-child(n)</td>
|
||||
<td><code>li:nth-child(2)</code></td>
|
||||
<td>Matches 2nd child</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>:nth-child(odd/even)</td>
|
||||
<td><code>tr:nth-child(odd)</code></td>
|
||||
<td>Matches odd rows</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>:first-of-type</td>
|
||||
<td><code>p:first-of-type</code></td>
|
||||
<td>First p among siblings</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>:last-of-type</td>
|
||||
<td><code>p:last-of-type</code></td>
|
||||
<td>Last p among siblings</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>:only-child</td>
|
||||
<td><code>p:only-child</code></td>
|
||||
<td>Matches if only child</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>:empty</td>
|
||||
<td><code>div:empty</code></td>
|
||||
<td>Matches elements with no children</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h3>Compound Selectors</h3>
|
||||
<pre><code>doc.querySelector("div.container#main") // Tag + class + ID
|
||||
doc.querySelector("input[type='text'].large") // Tag + attribute + class
|
||||
doc.querySelector("ul > li:first-child") // Combinator + pseudo-class</code></pre>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2>Examples</h2>
|
||||
|
||||
<h3>Building URLs</h3>
|
||||
<pre><code>import "html" for Html
|
||||
<h3>Web Scraping</h3>
|
||||
<pre><code>import "html" for Document
|
||||
|
||||
var baseUrl = "https://api.example.com/search"
|
||||
var params = {
|
||||
"query": "wren programming",
|
||||
"limit": 10,
|
||||
"offset": 0
|
||||
}
|
||||
var html = """
|
||||
<div class="products">
|
||||
<div class="product">
|
||||
<h2>Widget</h2>
|
||||
<span class="price">$9.99</span>
|
||||
</div>
|
||||
<div class="product">
|
||||
<h2>Gadget</h2>
|
||||
<span class="price">$19.99</span>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
|
||||
var url = baseUrl + "?" + Html.encodeParams(params)
|
||||
System.print(url)
|
||||
// https://api.example.com/search?query=wren+programming&limit=10&offset=0</code></pre>
|
||||
|
||||
<h3>Safe HTML Output</h3>
|
||||
<pre><code>import "html" for Html
|
||||
|
||||
var userInput = "<script>alert('xss')</script>"
|
||||
var safeHtml = "<div class=\"comment\">" + Html.quote(userInput) + "</div>"
|
||||
System.print(safeHtml)</code></pre>
|
||||
|
||||
<h3>Generating Slugs for URLs</h3>
|
||||
<pre><code>import "html" for Html
|
||||
|
||||
var title = "How to Build Web Apps with Wren!"
|
||||
var slug = Html.slugify(title)
|
||||
var url = "/blog/" + slug
|
||||
System.print(url) // /blog/how-to-build-web-apps-with-wren</code></pre>
|
||||
|
||||
<h3>Parsing Query Strings</h3>
|
||||
<pre><code>import "html" for Html
|
||||
|
||||
var queryString = "category=books&sort=price&order=asc"
|
||||
var params = Html.decodeParams(queryString)
|
||||
|
||||
for (key in params.keys) {
|
||||
System.print("%(key): %(params[key])")
|
||||
var doc = Document.parse(html)
|
||||
for (product in doc.querySelectorAll(".product")) {
|
||||
var name = product.querySelector("h2").textContent
|
||||
var price = product.querySelector(".price").textContent
|
||||
System.print("%(name): %(price)")
|
||||
}</code></pre>
|
||||
|
||||
<div class="admonition warning">
|
||||
<div class="admonition-title">Warning</div>
|
||||
<p>Always use <code>Html.quote()</code> when inserting user-provided content into HTML to prevent cross-site scripting (XSS) attacks.</p>
|
||||
<h3>HTML Generation</h3>
|
||||
<pre><code>import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id='root'></div>")
|
||||
var root = doc.getElementById("root")
|
||||
|
||||
var items = ["Apple", "Banana", "Cherry"]
|
||||
var ul = doc.createElement("ul")
|
||||
for (item in items) {
|
||||
var li = doc.createElement("li")
|
||||
li.textContent = item
|
||||
ul.appendChild(li)
|
||||
}
|
||||
root.appendChild(ul)
|
||||
|
||||
System.print(doc.outerHTML)</code></pre>
|
||||
|
||||
<h3>DOM Transformation</h3>
|
||||
<pre><code>import "html" for Document
|
||||
|
||||
var doc = Document.parse("<p>Hello <b>World</b></p>")
|
||||
|
||||
// Replace all <b> with <strong>
|
||||
for (b in doc.querySelectorAll("b").toList) {
|
||||
var strong = doc.createElement("strong")
|
||||
strong.innerHTML = b.innerHTML
|
||||
b.parentNode.replaceChild(strong, b)
|
||||
}
|
||||
|
||||
System.print(doc.outerHTML)</code></pre>
|
||||
|
||||
<div class="admonition note">
|
||||
<div class="admonition-title">Note</div>
|
||||
<p>The DOM implementation automatically decodes HTML entities when parsing and encodes them when serializing.</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@ -163,6 +163,7 @@ OBJECTS += $(OBJDIR)/pexpect.o
|
||||
OBJECTS += $(OBJDIR)/tls.o
|
||||
OBJECTS += $(OBJDIR)/udp_module.o
|
||||
OBJECTS += $(OBJDIR)/jinja.o
|
||||
OBJECTS += $(OBJDIR)/dom.o
|
||||
OBJECTS += $(OBJDIR)/stream.o
|
||||
OBJECTS += $(OBJDIR)/strscpy.o
|
||||
OBJECTS += $(OBJDIR)/sysinfo-loadavg.o
|
||||
@ -482,6 +483,9 @@ $(OBJDIR)/udp_module.o: ../../src/module/udp.c
|
||||
$(OBJDIR)/jinja.o: ../../src/module/jinja.c
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/dom.o: ../../src/module/dom.c
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
|
||||
-include $(OBJECTS:%.o=%.d)
|
||||
ifneq (,$(PCH))
|
||||
|
||||
@ -307,6 +307,93 @@ extern void jinjaWordcount(WrenVM* vm);
|
||||
extern void jinjaRenderNodes(WrenVM* vm);
|
||||
extern void jinjaTokenize(WrenVM* vm);
|
||||
extern void jinjaJsonEscape(WrenVM* vm);
|
||||
extern void domDocumentAllocate(WrenVM* vm);
|
||||
extern void domDocumentFinalize(void* data);
|
||||
extern void domDocumentQuerySelector(WrenVM* vm);
|
||||
extern void domDocumentQuerySelectorAll(WrenVM* vm);
|
||||
extern void domDocumentGetElementById(WrenVM* vm);
|
||||
extern void domDocumentGetElementsByClassName(WrenVM* vm);
|
||||
extern void domDocumentGetElementsByTagName(WrenVM* vm);
|
||||
extern void domDocumentCreateElement(WrenVM* vm);
|
||||
extern void domDocumentCreateTextNode(WrenVM* vm);
|
||||
extern void domDocumentBody(WrenVM* vm);
|
||||
extern void domDocumentHead(WrenVM* vm);
|
||||
extern void domDocumentDocumentElement(WrenVM* vm);
|
||||
extern void domDocumentTitle(WrenVM* vm);
|
||||
extern void domDocumentSetTitle(WrenVM* vm);
|
||||
extern void domDocumentOuterHTML(WrenVM* vm);
|
||||
extern void domDocumentInnerHTML(WrenVM* vm);
|
||||
extern void domElementAllocate(WrenVM* vm);
|
||||
extern void domElementFinalize(void* data);
|
||||
extern void domElementTagName(WrenVM* vm);
|
||||
extern void domElementId(WrenVM* vm);
|
||||
extern void domElementSetId(WrenVM* vm);
|
||||
extern void domElementClassName(WrenVM* vm);
|
||||
extern void domElementSetClassName(WrenVM* vm);
|
||||
extern void domElementClassList(WrenVM* vm);
|
||||
extern void domElementInnerHTML(WrenVM* vm);
|
||||
extern void domElementSetInnerHTML(WrenVM* vm);
|
||||
extern void domElementOuterHTML(WrenVM* vm);
|
||||
extern void domElementSetOuterHTML(WrenVM* vm);
|
||||
extern void domElementTextContent(WrenVM* vm);
|
||||
extern void domElementSetTextContent(WrenVM* vm);
|
||||
extern void domElementGetAttribute(WrenVM* vm);
|
||||
extern void domElementSetAttribute(WrenVM* vm);
|
||||
extern void domElementRemoveAttribute(WrenVM* vm);
|
||||
extern void domElementHasAttribute(WrenVM* vm);
|
||||
extern void domElementAttributes(WrenVM* vm);
|
||||
extern void domElementParentNode(WrenVM* vm);
|
||||
extern void domElementParentElement(WrenVM* vm);
|
||||
extern void domElementChildren(WrenVM* vm);
|
||||
extern void domElementChildNodes(WrenVM* vm);
|
||||
extern void domElementFirstChild(WrenVM* vm);
|
||||
extern void domElementLastChild(WrenVM* vm);
|
||||
extern void domElementFirstElementChild(WrenVM* vm);
|
||||
extern void domElementLastElementChild(WrenVM* vm);
|
||||
extern void domElementNextSibling(WrenVM* vm);
|
||||
extern void domElementPreviousSibling(WrenVM* vm);
|
||||
extern void domElementNextElementSibling(WrenVM* vm);
|
||||
extern void domElementPreviousElementSibling(WrenVM* vm);
|
||||
extern void domElementAppendChild(WrenVM* vm);
|
||||
extern void domElementInsertBefore(WrenVM* vm);
|
||||
extern void domElementRemoveChild(WrenVM* vm);
|
||||
extern void domElementReplaceChild(WrenVM* vm);
|
||||
extern void domElementCloneNode(WrenVM* vm);
|
||||
extern void domElementQuerySelector(WrenVM* vm);
|
||||
extern void domElementQuerySelectorAll(WrenVM* vm);
|
||||
extern void domElementGetElementsByClassName(WrenVM* vm);
|
||||
extern void domElementGetElementsByTagName(WrenVM* vm);
|
||||
extern void domElementMatches(WrenVM* vm);
|
||||
extern void domElementClosest(WrenVM* vm);
|
||||
extern void domElementRemove(WrenVM* vm);
|
||||
extern void domElementContains(WrenVM* vm);
|
||||
extern void domElementHasChildNodes(WrenVM* vm);
|
||||
extern void domElementNormalize(WrenVM* vm);
|
||||
extern void domElementNodeType(WrenVM* vm);
|
||||
extern void domElementNodeName(WrenVM* vm);
|
||||
extern void domElementNodeValue(WrenVM* vm);
|
||||
extern void domElementSetNodeValue(WrenVM* vm);
|
||||
extern void domElementDataset(WrenVM* vm);
|
||||
extern void domTextNodeAllocate(WrenVM* vm);
|
||||
extern void domTextNodeFinalize(void* data);
|
||||
extern void domTextNodeTextContent(WrenVM* vm);
|
||||
extern void domTextNodeSetTextContent(WrenVM* vm);
|
||||
extern void domTextNodeNodeType(WrenVM* vm);
|
||||
extern void domTextNodeNodeName(WrenVM* vm);
|
||||
extern void domTextNodeNodeValue(WrenVM* vm);
|
||||
extern void domTextNodeSetNodeValue(WrenVM* vm);
|
||||
extern void domTextNodeParentNode(WrenVM* vm);
|
||||
extern void domTextNodeParentElement(WrenVM* vm);
|
||||
extern void domTextNodeNextSibling(WrenVM* vm);
|
||||
extern void domTextNodePreviousSibling(WrenVM* vm);
|
||||
extern void domTextNodeCloneNode(WrenVM* vm);
|
||||
extern void domTextNodeRemove(WrenVM* vm);
|
||||
extern void domNodeListAllocate(WrenVM* vm);
|
||||
extern void domNodeListFinalize(void* data);
|
||||
extern void domNodeListCount(WrenVM* vm);
|
||||
extern void domNodeListSubscript(WrenVM* vm);
|
||||
extern void domNodeListItem(WrenVM* vm);
|
||||
extern void domNodeListToList(WrenVM* vm);
|
||||
|
||||
// The maximum number of foreign methods a single class defines. Ideally, we
|
||||
// would use variable-length arrays for each class in the table below, but
|
||||
@ -316,7 +403,7 @@ extern void jinjaJsonEscape(WrenVM* vm);
|
||||
// If you add a new method to the longest class below, make sure to bump this.
|
||||
// Note that it also includes an extra slot for the sentinel value indicating
|
||||
// the end of the list.
|
||||
#define MAX_METHODS_PER_CLASS 48
|
||||
#define MAX_METHODS_PER_CLASS 64
|
||||
|
||||
// The maximum number of foreign classes a single built-in module defines.
|
||||
//
|
||||
@ -441,6 +528,101 @@ static ModuleRegistry modules[] =
|
||||
END_CLASS
|
||||
END_MODULE
|
||||
MODULE(html)
|
||||
CLASS(Document)
|
||||
ALLOCATE(domDocumentAllocate)
|
||||
FINALIZE(domDocumentFinalize)
|
||||
METHOD("querySelector(_)", domDocumentQuerySelector)
|
||||
METHOD("querySelectorAll(_)", domDocumentQuerySelectorAll)
|
||||
METHOD("getElementById(_)", domDocumentGetElementById)
|
||||
METHOD("getElementsByClassName(_)", domDocumentGetElementsByClassName)
|
||||
METHOD("getElementsByTagName(_)", domDocumentGetElementsByTagName)
|
||||
METHOD("createElement(_)", domDocumentCreateElement)
|
||||
METHOD("createTextNode(_)", domDocumentCreateTextNode)
|
||||
METHOD("body", domDocumentBody)
|
||||
METHOD("head", domDocumentHead)
|
||||
METHOD("documentElement", domDocumentDocumentElement)
|
||||
METHOD("title", domDocumentTitle)
|
||||
METHOD("title=(_)", domDocumentSetTitle)
|
||||
METHOD("outerHTML", domDocumentOuterHTML)
|
||||
METHOD("innerHTML", domDocumentInnerHTML)
|
||||
END_CLASS
|
||||
CLASS(Element)
|
||||
ALLOCATE(domElementAllocate)
|
||||
FINALIZE(domElementFinalize)
|
||||
METHOD("tagName", domElementTagName)
|
||||
METHOD("id", domElementId)
|
||||
METHOD("id=(_)", domElementSetId)
|
||||
METHOD("className", domElementClassName)
|
||||
METHOD("className=(_)", domElementSetClassName)
|
||||
METHOD("classList", domElementClassList)
|
||||
METHOD("innerHTML", domElementInnerHTML)
|
||||
METHOD("innerHTML=(_)", domElementSetInnerHTML)
|
||||
METHOD("outerHTML", domElementOuterHTML)
|
||||
METHOD("outerHTML=(_)", domElementSetOuterHTML)
|
||||
METHOD("textContent", domElementTextContent)
|
||||
METHOD("textContent=(_)", domElementSetTextContent)
|
||||
METHOD("getAttribute(_)", domElementGetAttribute)
|
||||
METHOD("setAttribute(_,_)", domElementSetAttribute)
|
||||
METHOD("removeAttribute(_)", domElementRemoveAttribute)
|
||||
METHOD("hasAttribute(_)", domElementHasAttribute)
|
||||
METHOD("attributes", domElementAttributes)
|
||||
METHOD("parentNode", domElementParentNode)
|
||||
METHOD("parentElement", domElementParentElement)
|
||||
METHOD("children", domElementChildren)
|
||||
METHOD("childNodes", domElementChildNodes)
|
||||
METHOD("firstChild", domElementFirstChild)
|
||||
METHOD("lastChild", domElementLastChild)
|
||||
METHOD("firstElementChild", domElementFirstElementChild)
|
||||
METHOD("lastElementChild", domElementLastElementChild)
|
||||
METHOD("nextSibling", domElementNextSibling)
|
||||
METHOD("previousSibling", domElementPreviousSibling)
|
||||
METHOD("nextElementSibling", domElementNextElementSibling)
|
||||
METHOD("previousElementSibling", domElementPreviousElementSibling)
|
||||
METHOD("appendChild(_)", domElementAppendChild)
|
||||
METHOD("insertBefore(_,_)", domElementInsertBefore)
|
||||
METHOD("removeChild(_)", domElementRemoveChild)
|
||||
METHOD("replaceChild(_,_)", domElementReplaceChild)
|
||||
METHOD("cloneNode(_)", domElementCloneNode)
|
||||
METHOD("querySelector(_)", domElementQuerySelector)
|
||||
METHOD("querySelectorAll(_)", domElementQuerySelectorAll)
|
||||
METHOD("getElementsByClassName(_)", domElementGetElementsByClassName)
|
||||
METHOD("getElementsByTagName(_)", domElementGetElementsByTagName)
|
||||
METHOD("matches(_)", domElementMatches)
|
||||
METHOD("closest(_)", domElementClosest)
|
||||
METHOD("remove()", domElementRemove)
|
||||
METHOD("contains(_)", domElementContains)
|
||||
METHOD("hasChildNodes()", domElementHasChildNodes)
|
||||
METHOD("normalize()", domElementNormalize)
|
||||
METHOD("nodeType", domElementNodeType)
|
||||
METHOD("nodeName", domElementNodeName)
|
||||
METHOD("nodeValue", domElementNodeValue)
|
||||
METHOD("nodeValue=(_)", domElementSetNodeValue)
|
||||
METHOD("dataset", domElementDataset)
|
||||
END_CLASS
|
||||
CLASS(TextNode)
|
||||
ALLOCATE(domTextNodeAllocate)
|
||||
FINALIZE(domTextNodeFinalize)
|
||||
METHOD("textContent", domTextNodeTextContent)
|
||||
METHOD("textContent=(_)", domTextNodeSetTextContent)
|
||||
METHOD("nodeType", domTextNodeNodeType)
|
||||
METHOD("nodeName", domTextNodeNodeName)
|
||||
METHOD("nodeValue", domTextNodeNodeValue)
|
||||
METHOD("nodeValue=(_)", domTextNodeSetNodeValue)
|
||||
METHOD("parentNode", domTextNodeParentNode)
|
||||
METHOD("parentElement", domTextNodeParentElement)
|
||||
METHOD("nextSibling", domTextNodeNextSibling)
|
||||
METHOD("previousSibling", domTextNodePreviousSibling)
|
||||
METHOD("cloneNode(_)", domTextNodeCloneNode)
|
||||
METHOD("remove()", domTextNodeRemove)
|
||||
END_CLASS
|
||||
CLASS(NodeList)
|
||||
ALLOCATE(domNodeListAllocate)
|
||||
FINALIZE(domNodeListFinalize)
|
||||
METHOD("count", domNodeListCount)
|
||||
METHOD("[_]", domNodeListSubscript)
|
||||
METHOD("item(_)", domNodeListItem)
|
||||
METHOD("toList", domNodeListToList)
|
||||
END_CLASS
|
||||
END_MODULE
|
||||
MODULE(http)
|
||||
END_MODULE
|
||||
|
||||
2202
src/module/dom.c
Normal file
2202
src/module/dom.c
Normal file
File diff suppressed because it is too large
Load Diff
143
src/module/dom.h
Normal file
143
src/module/dom.h
Normal file
@ -0,0 +1,143 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
#ifndef dom_h
|
||||
#define dom_h
|
||||
|
||||
#include "wren.h"
|
||||
|
||||
typedef enum {
|
||||
DOM_ELEMENT_NODE = 1,
|
||||
DOM_TEXT_NODE = 3,
|
||||
DOM_COMMENT_NODE = 8,
|
||||
DOM_DOCUMENT_NODE = 9
|
||||
} DomNodeType;
|
||||
|
||||
typedef struct DomNode DomNode;
|
||||
typedef struct DomDocument DomDocument;
|
||||
|
||||
struct DomNode {
|
||||
DomNodeType type;
|
||||
char* tagName;
|
||||
char* textContent;
|
||||
DomNode* parent;
|
||||
DomNode* firstChild;
|
||||
DomNode* lastChild;
|
||||
DomNode* nextSibling;
|
||||
DomNode* prevSibling;
|
||||
char** attrNames;
|
||||
char** attrValues;
|
||||
int attrCount;
|
||||
int attrCapacity;
|
||||
DomDocument* ownerDocument;
|
||||
};
|
||||
|
||||
struct DomDocument {
|
||||
DomNode* root;
|
||||
DomNode* documentElement;
|
||||
DomNode* head;
|
||||
DomNode* body;
|
||||
WrenVM* vm;
|
||||
WrenHandle* elementClass;
|
||||
WrenHandle* textNodeClass;
|
||||
WrenHandle* nodeListClass;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
DomNode** nodes;
|
||||
int count;
|
||||
int capacity;
|
||||
DomDocument* ownerDocument;
|
||||
} DomNodeList;
|
||||
|
||||
void domDocumentAllocate(WrenVM* vm);
|
||||
void domDocumentFinalize(void* data);
|
||||
void domDocumentQuerySelector(WrenVM* vm);
|
||||
void domDocumentQuerySelectorAll(WrenVM* vm);
|
||||
void domDocumentGetElementById(WrenVM* vm);
|
||||
void domDocumentGetElementsByClassName(WrenVM* vm);
|
||||
void domDocumentGetElementsByTagName(WrenVM* vm);
|
||||
void domDocumentCreateElement(WrenVM* vm);
|
||||
void domDocumentCreateTextNode(WrenVM* vm);
|
||||
void domDocumentBody(WrenVM* vm);
|
||||
void domDocumentHead(WrenVM* vm);
|
||||
void domDocumentDocumentElement(WrenVM* vm);
|
||||
void domDocumentTitle(WrenVM* vm);
|
||||
void domDocumentSetTitle(WrenVM* vm);
|
||||
void domDocumentOuterHTML(WrenVM* vm);
|
||||
void domDocumentInnerHTML(WrenVM* vm);
|
||||
|
||||
void domElementAllocate(WrenVM* vm);
|
||||
void domElementFinalize(void* data);
|
||||
void domElementTagName(WrenVM* vm);
|
||||
void domElementId(WrenVM* vm);
|
||||
void domElementSetId(WrenVM* vm);
|
||||
void domElementClassName(WrenVM* vm);
|
||||
void domElementSetClassName(WrenVM* vm);
|
||||
void domElementClassList(WrenVM* vm);
|
||||
void domElementInnerHTML(WrenVM* vm);
|
||||
void domElementSetInnerHTML(WrenVM* vm);
|
||||
void domElementOuterHTML(WrenVM* vm);
|
||||
void domElementSetOuterHTML(WrenVM* vm);
|
||||
void domElementTextContent(WrenVM* vm);
|
||||
void domElementSetTextContent(WrenVM* vm);
|
||||
void domElementGetAttribute(WrenVM* vm);
|
||||
void domElementSetAttribute(WrenVM* vm);
|
||||
void domElementRemoveAttribute(WrenVM* vm);
|
||||
void domElementHasAttribute(WrenVM* vm);
|
||||
void domElementAttributes(WrenVM* vm);
|
||||
void domElementParentNode(WrenVM* vm);
|
||||
void domElementParentElement(WrenVM* vm);
|
||||
void domElementChildren(WrenVM* vm);
|
||||
void domElementChildNodes(WrenVM* vm);
|
||||
void domElementFirstChild(WrenVM* vm);
|
||||
void domElementLastChild(WrenVM* vm);
|
||||
void domElementFirstElementChild(WrenVM* vm);
|
||||
void domElementLastElementChild(WrenVM* vm);
|
||||
void domElementNextSibling(WrenVM* vm);
|
||||
void domElementPreviousSibling(WrenVM* vm);
|
||||
void domElementNextElementSibling(WrenVM* vm);
|
||||
void domElementPreviousElementSibling(WrenVM* vm);
|
||||
void domElementAppendChild(WrenVM* vm);
|
||||
void domElementInsertBefore(WrenVM* vm);
|
||||
void domElementRemoveChild(WrenVM* vm);
|
||||
void domElementReplaceChild(WrenVM* vm);
|
||||
void domElementCloneNode(WrenVM* vm);
|
||||
void domElementQuerySelector(WrenVM* vm);
|
||||
void domElementQuerySelectorAll(WrenVM* vm);
|
||||
void domElementGetElementsByClassName(WrenVM* vm);
|
||||
void domElementGetElementsByTagName(WrenVM* vm);
|
||||
void domElementMatches(WrenVM* vm);
|
||||
void domElementClosest(WrenVM* vm);
|
||||
void domElementRemove(WrenVM* vm);
|
||||
void domElementContains(WrenVM* vm);
|
||||
void domElementHasChildNodes(WrenVM* vm);
|
||||
void domElementNormalize(WrenVM* vm);
|
||||
void domElementNodeType(WrenVM* vm);
|
||||
void domElementNodeName(WrenVM* vm);
|
||||
void domElementNodeValue(WrenVM* vm);
|
||||
void domElementSetNodeValue(WrenVM* vm);
|
||||
void domElementDataset(WrenVM* vm);
|
||||
|
||||
void domTextNodeAllocate(WrenVM* vm);
|
||||
void domTextNodeFinalize(void* data);
|
||||
void domTextNodeTextContent(WrenVM* vm);
|
||||
void domTextNodeSetTextContent(WrenVM* vm);
|
||||
void domTextNodeNodeType(WrenVM* vm);
|
||||
void domTextNodeNodeName(WrenVM* vm);
|
||||
void domTextNodeNodeValue(WrenVM* vm);
|
||||
void domTextNodeSetNodeValue(WrenVM* vm);
|
||||
void domTextNodeParentNode(WrenVM* vm);
|
||||
void domTextNodeParentElement(WrenVM* vm);
|
||||
void domTextNodeNextSibling(WrenVM* vm);
|
||||
void domTextNodePreviousSibling(WrenVM* vm);
|
||||
void domTextNodeCloneNode(WrenVM* vm);
|
||||
void domTextNodeRemove(WrenVM* vm);
|
||||
|
||||
void domNodeListAllocate(WrenVM* vm);
|
||||
void domNodeListFinalize(void* data);
|
||||
void domNodeListCount(WrenVM* vm);
|
||||
void domNodeListSubscript(WrenVM* vm);
|
||||
void domNodeListItem(WrenVM* vm);
|
||||
void domNodeListToList(WrenVM* vm);
|
||||
|
||||
#endif
|
||||
113
src/module/html.wren
vendored
113
src/module/html.wren
vendored
@ -128,3 +128,116 @@ class Html {
|
||||
return params
|
||||
}
|
||||
}
|
||||
|
||||
foreign class Document {
|
||||
construct parse(html) {}
|
||||
|
||||
foreign querySelector(selector)
|
||||
foreign querySelectorAll(selector)
|
||||
foreign getElementById(id)
|
||||
foreign getElementsByClassName(className)
|
||||
foreign getElementsByTagName(tagName)
|
||||
foreign createElement(tagName)
|
||||
foreign createTextNode(text)
|
||||
foreign body
|
||||
foreign head
|
||||
foreign documentElement
|
||||
foreign title
|
||||
foreign title=(value)
|
||||
foreign outerHTML
|
||||
foreign innerHTML
|
||||
}
|
||||
|
||||
foreign class Element {
|
||||
construct new_() {}
|
||||
|
||||
foreign tagName
|
||||
foreign id
|
||||
foreign id=(value)
|
||||
foreign className
|
||||
foreign className=(value)
|
||||
foreign classList
|
||||
foreign innerHTML
|
||||
foreign innerHTML=(value)
|
||||
foreign outerHTML
|
||||
foreign outerHTML=(value)
|
||||
foreign textContent
|
||||
foreign textContent=(value)
|
||||
foreign getAttribute(name)
|
||||
foreign setAttribute(name, value)
|
||||
foreign removeAttribute(name)
|
||||
foreign hasAttribute(name)
|
||||
foreign attributes
|
||||
foreign parentNode
|
||||
foreign parentElement
|
||||
foreign children
|
||||
foreign childNodes
|
||||
foreign firstChild
|
||||
foreign lastChild
|
||||
foreign firstElementChild
|
||||
foreign lastElementChild
|
||||
foreign nextSibling
|
||||
foreign previousSibling
|
||||
foreign nextElementSibling
|
||||
foreign previousElementSibling
|
||||
foreign appendChild(child)
|
||||
foreign insertBefore(newNode, refNode)
|
||||
foreign removeChild(child)
|
||||
foreign replaceChild(newChild, oldChild)
|
||||
foreign cloneNode(deep)
|
||||
foreign querySelector(selector)
|
||||
foreign querySelectorAll(selector)
|
||||
foreign getElementsByClassName(className)
|
||||
foreign getElementsByTagName(tagName)
|
||||
foreign matches(selector)
|
||||
foreign closest(selector)
|
||||
foreign remove()
|
||||
foreign contains(node)
|
||||
foreign hasChildNodes()
|
||||
foreign normalize()
|
||||
foreign nodeType
|
||||
foreign nodeName
|
||||
foreign nodeValue
|
||||
foreign nodeValue=(value)
|
||||
foreign dataset
|
||||
}
|
||||
|
||||
foreign class TextNode {
|
||||
construct new_() {}
|
||||
|
||||
foreign textContent
|
||||
foreign textContent=(value)
|
||||
foreign nodeType
|
||||
foreign nodeName
|
||||
foreign nodeValue
|
||||
foreign nodeValue=(value)
|
||||
foreign parentNode
|
||||
foreign parentElement
|
||||
foreign nextSibling
|
||||
foreign previousSibling
|
||||
foreign cloneNode(deep)
|
||||
foreign remove()
|
||||
}
|
||||
|
||||
foreign class NodeList {
|
||||
construct new_() {}
|
||||
|
||||
foreign count
|
||||
foreign [index]
|
||||
foreign item(index)
|
||||
foreign toList
|
||||
|
||||
iterate(iterator) {
|
||||
if (iterator == null) return 0
|
||||
if (iterator >= count - 1) return false
|
||||
return iterator + 1
|
||||
}
|
||||
|
||||
iteratorValue(iterator) { this[iterator] }
|
||||
|
||||
forEach(fn) {
|
||||
for (node in this) {
|
||||
fn.call(node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,4 +131,117 @@ static const char* htmlModuleSource =
|
||||
" }\n"
|
||||
" return params\n"
|
||||
" }\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"foreign class Document {\n"
|
||||
" construct parse(html) {}\n"
|
||||
"\n"
|
||||
" foreign querySelector(selector)\n"
|
||||
" foreign querySelectorAll(selector)\n"
|
||||
" foreign getElementById(id)\n"
|
||||
" foreign getElementsByClassName(className)\n"
|
||||
" foreign getElementsByTagName(tagName)\n"
|
||||
" foreign createElement(tagName)\n"
|
||||
" foreign createTextNode(text)\n"
|
||||
" foreign body\n"
|
||||
" foreign head\n"
|
||||
" foreign documentElement\n"
|
||||
" foreign title\n"
|
||||
" foreign title=(value)\n"
|
||||
" foreign outerHTML\n"
|
||||
" foreign innerHTML\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"foreign class Element {\n"
|
||||
" construct new_() {}\n"
|
||||
"\n"
|
||||
" foreign tagName\n"
|
||||
" foreign id\n"
|
||||
" foreign id=(value)\n"
|
||||
" foreign className\n"
|
||||
" foreign className=(value)\n"
|
||||
" foreign classList\n"
|
||||
" foreign innerHTML\n"
|
||||
" foreign innerHTML=(value)\n"
|
||||
" foreign outerHTML\n"
|
||||
" foreign outerHTML=(value)\n"
|
||||
" foreign textContent\n"
|
||||
" foreign textContent=(value)\n"
|
||||
" foreign getAttribute(name)\n"
|
||||
" foreign setAttribute(name, value)\n"
|
||||
" foreign removeAttribute(name)\n"
|
||||
" foreign hasAttribute(name)\n"
|
||||
" foreign attributes\n"
|
||||
" foreign parentNode\n"
|
||||
" foreign parentElement\n"
|
||||
" foreign children\n"
|
||||
" foreign childNodes\n"
|
||||
" foreign firstChild\n"
|
||||
" foreign lastChild\n"
|
||||
" foreign firstElementChild\n"
|
||||
" foreign lastElementChild\n"
|
||||
" foreign nextSibling\n"
|
||||
" foreign previousSibling\n"
|
||||
" foreign nextElementSibling\n"
|
||||
" foreign previousElementSibling\n"
|
||||
" foreign appendChild(child)\n"
|
||||
" foreign insertBefore(newNode, refNode)\n"
|
||||
" foreign removeChild(child)\n"
|
||||
" foreign replaceChild(newChild, oldChild)\n"
|
||||
" foreign cloneNode(deep)\n"
|
||||
" foreign querySelector(selector)\n"
|
||||
" foreign querySelectorAll(selector)\n"
|
||||
" foreign getElementsByClassName(className)\n"
|
||||
" foreign getElementsByTagName(tagName)\n"
|
||||
" foreign matches(selector)\n"
|
||||
" foreign closest(selector)\n"
|
||||
" foreign remove()\n"
|
||||
" foreign contains(node)\n"
|
||||
" foreign hasChildNodes()\n"
|
||||
" foreign normalize()\n"
|
||||
" foreign nodeType\n"
|
||||
" foreign nodeName\n"
|
||||
" foreign nodeValue\n"
|
||||
" foreign nodeValue=(value)\n"
|
||||
" foreign dataset\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"foreign class TextNode {\n"
|
||||
" construct new_() {}\n"
|
||||
"\n"
|
||||
" foreign textContent\n"
|
||||
" foreign textContent=(value)\n"
|
||||
" foreign nodeType\n"
|
||||
" foreign nodeName\n"
|
||||
" foreign nodeValue\n"
|
||||
" foreign nodeValue=(value)\n"
|
||||
" foreign parentNode\n"
|
||||
" foreign parentElement\n"
|
||||
" foreign nextSibling\n"
|
||||
" foreign previousSibling\n"
|
||||
" foreign cloneNode(deep)\n"
|
||||
" foreign remove()\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"foreign class NodeList {\n"
|
||||
" construct new_() {}\n"
|
||||
"\n"
|
||||
" foreign count\n"
|
||||
" foreign [index]\n"
|
||||
" foreign item(index)\n"
|
||||
" foreign toList\n"
|
||||
"\n"
|
||||
" iterate(iterator) {\n"
|
||||
" if (iterator == null) return 0\n"
|
||||
" if (iterator >= count - 1) return false\n"
|
||||
" return iterator + 1\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" iteratorValue(iterator) { this[iterator] }\n"
|
||||
"\n"
|
||||
" forEach(fn) {\n"
|
||||
" for (node in this) {\n"
|
||||
" fn.call(node)\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
"}\n";
|
||||
|
||||
472
src/module/markdown.wren
vendored
472
src/module/markdown.wren
vendored
@ -645,111 +645,181 @@ class Markdown {
|
||||
static fromHtml(html) { fromHtml(html, {}) }
|
||||
|
||||
static fromHtml(html, options) {
|
||||
var stripUnknown = options.containsKey("stripUnknown") ? options["stripUnknown"] : true
|
||||
var text = html
|
||||
|
||||
text = removeHiddenTags_(text)
|
||||
var text = removeHiddenTagsFast_(html)
|
||||
text = processBlockElements_(text)
|
||||
text = processInlineFromHtml_(text)
|
||||
text = Html.unquote(text)
|
||||
text = cleanupWhitespace_(text)
|
||||
|
||||
return text.trim()
|
||||
}
|
||||
|
||||
static removeHiddenTags_(html) {
|
||||
var result = html
|
||||
var tags = ["script", "style", "head", "meta", "link", "noscript"]
|
||||
for (tag in tags) {
|
||||
result = removeTagWithContent_(result, tag)
|
||||
}
|
||||
return result
|
||||
}
|
||||
static removeHiddenTagsFast_(html) {
|
||||
var hiddenTags = {"script": true, "style": true, "head": true, "meta": true, "link": true, "noscript": true}
|
||||
var parts = []
|
||||
var pos = 0
|
||||
var len = html.count
|
||||
|
||||
static removeTagWithContent_(html, tag) {
|
||||
var result = html
|
||||
var openTag = "<" + tag
|
||||
var closeTag = "</" + tag + ">"
|
||||
while (true) {
|
||||
var startLower = result.indexOf(openTag)
|
||||
var startUpper = result.indexOf("<" + tag[0].codePoints.toList[0].toString)
|
||||
var start = -1
|
||||
while (pos < len) {
|
||||
var tagStart = indexOfFrom_(html, "<", pos)
|
||||
if (tagStart < 0) {
|
||||
parts.add(html[pos..-1])
|
||||
break
|
||||
}
|
||||
|
||||
var i = 0
|
||||
while (i < result.count - openTag.count) {
|
||||
if (matchTagName_(result, i, tag)) {
|
||||
start = i
|
||||
break
|
||||
var tagInfo = parseTagFast_(html, tagStart)
|
||||
var tagName = tagInfo["name"]
|
||||
var tagEnd = tagInfo["end"]
|
||||
var isClose = tagInfo["isClose"]
|
||||
var isSelfClose = tagInfo["isSelfClose"]
|
||||
|
||||
if (hiddenTags.containsKey(tagName)) {
|
||||
if (tagStart > pos) {
|
||||
parts.add(html[pos...tagStart])
|
||||
}
|
||||
if (isSelfClose || isClose) {
|
||||
pos = tagEnd
|
||||
continue
|
||||
}
|
||||
var closePos = findCloseTagFast_(html, tagEnd, tagName)
|
||||
if (closePos < 0) {
|
||||
pos = tagEnd
|
||||
} else {
|
||||
var closeEnd = indexOfFrom_(html, ">", closePos)
|
||||
pos = (closeEnd >= 0) ? closeEnd + 1 : closePos
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
|
||||
if (start < 0) break
|
||||
|
||||
var tagEnd = start
|
||||
while (tagEnd < result.count && result[tagEnd] != ">") {
|
||||
tagEnd = tagEnd + 1
|
||||
}
|
||||
if (tagEnd >= result.count) break
|
||||
|
||||
if (result[tagEnd - 1] == "/") {
|
||||
result = result[0...start] + result[tagEnd + 1..-1]
|
||||
continue
|
||||
}
|
||||
|
||||
var closeStart = findCloseTag_(result, tagEnd + 1, tag)
|
||||
if (closeStart < 0) {
|
||||
result = result[0...start] + result[tagEnd + 1..-1]
|
||||
} else {
|
||||
var closeEnd = closeStart
|
||||
while (closeEnd < result.count && result[closeEnd] != ">") {
|
||||
closeEnd = closeEnd + 1
|
||||
}
|
||||
result = result[0...start] + result[closeEnd + 1..-1]
|
||||
parts.add(html[pos...tagEnd])
|
||||
pos = tagEnd
|
||||
}
|
||||
|
||||
return parts.join("")
|
||||
}
|
||||
|
||||
static indexOfFrom_(str, char, start) {
|
||||
var len = str.count
|
||||
var i = start
|
||||
while (i < len) {
|
||||
if (str[i] == char) return i
|
||||
i = i + 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
static parseTagFast_(html, pos) {
|
||||
var len = html.count
|
||||
var result = {"name": "", "end": pos + 1, "isClose": false, "isSelfClose": false}
|
||||
if (pos >= len || html[pos] != "<") return result
|
||||
|
||||
var i = pos + 1
|
||||
while (i < len && (html[i] == " " || html[i] == "\t" || html[i] == "\n")) {
|
||||
i = i + 1
|
||||
}
|
||||
|
||||
var isClose = false
|
||||
if (i < len && html[i] == "/") {
|
||||
isClose = true
|
||||
i = i + 1
|
||||
while (i < len && (html[i] == " " || html[i] == "\t" || html[i] == "\n")) {
|
||||
i = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
var nameStart = i
|
||||
while (i < len) {
|
||||
var c = html[i]
|
||||
if (c == " " || c == ">" || c == "/" || c == "\t" || c == "\n") break
|
||||
i = i + 1
|
||||
}
|
||||
|
||||
var name = ""
|
||||
if (i > nameStart) {
|
||||
name = toLower_(html[nameStart...i])
|
||||
}
|
||||
|
||||
var tagEnd = indexOfFrom_(html, ">", i)
|
||||
if (tagEnd < 0) tagEnd = len
|
||||
var isSelfClose = tagEnd > 0 && html[tagEnd - 1] == "/"
|
||||
|
||||
result["name"] = name
|
||||
result["end"] = tagEnd + 1
|
||||
result["isClose"] = isClose
|
||||
result["isSelfClose"] = isSelfClose
|
||||
return result
|
||||
}
|
||||
|
||||
static toLower_(str) {
|
||||
var parts = []
|
||||
for (c in str) {
|
||||
var code = c.bytes[0]
|
||||
if (code >= 65 && code <= 90) {
|
||||
parts.add(String.fromCodePoint(code + 32))
|
||||
} else {
|
||||
parts.add(c)
|
||||
}
|
||||
}
|
||||
return parts.join("")
|
||||
}
|
||||
|
||||
static findCloseTagFast_(html, start, tag) {
|
||||
var len = html.count
|
||||
var tagLen = tag.count
|
||||
var i = start
|
||||
while (i < len - tagLen - 2) {
|
||||
var idx = indexOfFrom_(html, "<", i)
|
||||
if (idx < 0) return -1
|
||||
if (idx + 1 < len && html[idx + 1] == "/") {
|
||||
var match = true
|
||||
var j = 0
|
||||
while (j < tagLen && idx + 2 + j < len) {
|
||||
var c1 = html[idx + 2 + j].bytes[0]
|
||||
var c2 = tag[j].bytes[0]
|
||||
if (c1 >= 65 && c1 <= 90) c1 = c1 + 32
|
||||
if (c1 != c2) {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
j = j + 1
|
||||
}
|
||||
if (match && j == tagLen) {
|
||||
var next = idx + 2 + tagLen
|
||||
if (next >= len || html[next] == ">" || html[next] == " " || html[next] == "\t" || html[next] == "\n") {
|
||||
return idx
|
||||
}
|
||||
}
|
||||
}
|
||||
i = idx + 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
static matchTagName_(html, pos, tag) {
|
||||
if (pos >= html.count || html[pos] != "<") return false
|
||||
var rest = html[pos + 1..-1].trim()
|
||||
var tagLower = tag
|
||||
for (i in 0...tag.count) {
|
||||
if (i >= rest.count) return false
|
||||
var c1 = rest[i].codePoints.toList[0]
|
||||
var c2 = tag[i].codePoints.toList[0]
|
||||
var len = html.count
|
||||
var tagLen = tag.count
|
||||
if (pos >= len || html[pos] != "<") return false
|
||||
var i = pos + 1
|
||||
while (i < len && (html[i] == " " || html[i] == "\t" || html[i] == "\n")) {
|
||||
i = i + 1
|
||||
}
|
||||
for (j in 0...tagLen) {
|
||||
if (i + j >= len) return false
|
||||
var c1 = html[i + j].bytes[0]
|
||||
var c2 = tag[j].bytes[0]
|
||||
if (c1 >= 65 && c1 <= 90) c1 = c1 + 32
|
||||
if (c2 >= 65 && c2 <= 90) c2 = c2 + 32
|
||||
if (c1 != c2) return false
|
||||
}
|
||||
if (tag.count < rest.count) {
|
||||
var next = rest[tag.count]
|
||||
var nextPos = i + tagLen
|
||||
if (nextPos < len) {
|
||||
var next = html[nextPos]
|
||||
if (next != " " && next != ">" && next != "/" && next != "\t" && next != "\n") return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
static findCloseTag_(html, start, tag) {
|
||||
var i = start
|
||||
while (i < html.count - tag.count - 2) {
|
||||
if (html[i] == "<" && html[i + 1] == "/") {
|
||||
var match = true
|
||||
for (j in 0...tag.count) {
|
||||
var c1 = html[i + 2 + j].codePoints.toList[0]
|
||||
var c2 = tag[j].codePoints.toList[0]
|
||||
if (c1 >= 65 && c1 <= 90) c1 = c1 + 32
|
||||
if (c2 >= 65 && c2 <= 90) c2 = c2 + 32
|
||||
if (c1 != c2) {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if (match) return i
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return -1
|
||||
return findCloseTagFast_(html, start, tag)
|
||||
}
|
||||
|
||||
static processBlockElements_(html) {
|
||||
@ -1142,52 +1212,63 @@ class Markdown {
|
||||
|
||||
static findTagStartFrom_(html, start, tag) {
|
||||
var i = start
|
||||
while (i < html.count) {
|
||||
if (matchTagName_(html, i, tag)) return i
|
||||
i = i + 1
|
||||
var len = html.count
|
||||
while (i < len) {
|
||||
var idx = indexOfFrom_(html, "<", i)
|
||||
if (idx < 0) return -1
|
||||
if (matchTagName_(html, idx, tag)) return idx
|
||||
i = idx + 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
static processHrFromHtml_(html) {
|
||||
var parts = []
|
||||
var i = 0
|
||||
while (i < html.count) {
|
||||
if (matchTagName_(html, i, "hr")) {
|
||||
var end = i
|
||||
while (end < html.count && html[end] != ">") {
|
||||
end = end + 1
|
||||
}
|
||||
var pos = 0
|
||||
var len = html.count
|
||||
while (pos < len) {
|
||||
var tagStart = indexOfFrom_(html, "<", pos)
|
||||
if (tagStart < 0) {
|
||||
parts.add(html[pos..-1])
|
||||
break
|
||||
}
|
||||
if (matchTagName_(html, tagStart, "hr")) {
|
||||
if (tagStart > pos) parts.add(html[pos...tagStart])
|
||||
var end = indexOfFrom_(html, ">", tagStart)
|
||||
if (end < 0) end = len - 1
|
||||
parts.add("\n---\n")
|
||||
i = end + 1
|
||||
pos = end + 1
|
||||
} else {
|
||||
parts.add(html[i])
|
||||
i = i + 1
|
||||
parts.add(html[pos...tagStart + 1])
|
||||
pos = tagStart + 1
|
||||
}
|
||||
}
|
||||
return parts.join("")
|
||||
}
|
||||
|
||||
static processParagraphsFromHtml_(html) {
|
||||
var result = html
|
||||
result = replaceTag_(result, "p", "", "\n\n")
|
||||
return result
|
||||
return replaceTag_(html, "p", "", "\n\n")
|
||||
}
|
||||
|
||||
static processBrFromHtml_(html) {
|
||||
var parts = []
|
||||
var i = 0
|
||||
while (i < html.count) {
|
||||
if (matchTagName_(html, i, "br")) {
|
||||
var end = i
|
||||
while (end < html.count && html[end] != ">") {
|
||||
end = end + 1
|
||||
}
|
||||
var pos = 0
|
||||
var len = html.count
|
||||
while (pos < len) {
|
||||
var tagStart = indexOfFrom_(html, "<", pos)
|
||||
if (tagStart < 0) {
|
||||
parts.add(html[pos..-1])
|
||||
break
|
||||
}
|
||||
if (matchTagName_(html, tagStart, "br")) {
|
||||
if (tagStart > pos) parts.add(html[pos...tagStart])
|
||||
var end = indexOfFrom_(html, ">", tagStart)
|
||||
if (end < 0) end = len - 1
|
||||
parts.add("\n")
|
||||
i = end + 1
|
||||
pos = end + 1
|
||||
} else {
|
||||
parts.add(html[i])
|
||||
i = i + 1
|
||||
parts.add(html[pos...tagStart + 1])
|
||||
pos = tagStart + 1
|
||||
}
|
||||
}
|
||||
return parts.join("")
|
||||
@ -1242,71 +1323,95 @@ class Markdown {
|
||||
}
|
||||
|
||||
static processLinksFromHtml_(html) {
|
||||
var result = html
|
||||
var parts = []
|
||||
var pos = 0
|
||||
var len = html.count
|
||||
|
||||
while (true) {
|
||||
var start = findTagStart_(result, "a")
|
||||
if (start < 0) break
|
||||
|
||||
var openEnd = start
|
||||
while (openEnd < result.count && result[openEnd] != ">") {
|
||||
openEnd = openEnd + 1
|
||||
while (pos < len) {
|
||||
var start = findTagStartFrom_(html, pos, "a")
|
||||
if (start < 0) {
|
||||
parts.add(html[pos..-1])
|
||||
break
|
||||
}
|
||||
if (openEnd >= result.count) break
|
||||
|
||||
var tagContent = result[start...openEnd + 1]
|
||||
if (start > pos) {
|
||||
parts.add(html[pos...start])
|
||||
}
|
||||
|
||||
var openEnd = indexOfFrom_(html, ">", start)
|
||||
if (openEnd < 0) {
|
||||
parts.add(html[pos..-1])
|
||||
break
|
||||
}
|
||||
|
||||
var tagContent = html[start...openEnd + 1]
|
||||
var href = extractAttr_(tagContent, "href")
|
||||
|
||||
var closeStart = findCloseTag_(result, openEnd + 1, "a")
|
||||
if (closeStart < 0) break
|
||||
|
||||
var linkText = result[openEnd + 1...closeStart]
|
||||
|
||||
var closeEnd = closeStart
|
||||
while (closeEnd < result.count && result[closeEnd] != ">") {
|
||||
closeEnd = closeEnd + 1
|
||||
var closeStart = findCloseTagFast_(html, openEnd + 1, "a")
|
||||
if (closeStart < 0) {
|
||||
parts.add(html[pos...openEnd + 1])
|
||||
pos = openEnd + 1
|
||||
continue
|
||||
}
|
||||
|
||||
var before = result[0...start]
|
||||
var after = result[closeEnd + 1..-1]
|
||||
var linkText = html[openEnd + 1...closeStart]
|
||||
var closeEnd = indexOfFrom_(html, ">", closeStart)
|
||||
if (closeEnd < 0) closeEnd = len - 1
|
||||
|
||||
if (href != null && href.count > 0) {
|
||||
result = before + "[" + stripAllTags_(linkText) + "](" + href + ")" + after
|
||||
parts.add("[")
|
||||
parts.add(stripAllTags_(linkText))
|
||||
parts.add("](")
|
||||
parts.add(href)
|
||||
parts.add(")")
|
||||
} else {
|
||||
result = before + stripAllTags_(linkText) + after
|
||||
parts.add(stripAllTags_(linkText))
|
||||
}
|
||||
pos = closeEnd + 1
|
||||
}
|
||||
|
||||
return result
|
||||
return parts.join("")
|
||||
}
|
||||
|
||||
static processImagesFromHtml_(html) {
|
||||
var result = html
|
||||
var i = 0
|
||||
var parts = []
|
||||
var pos = 0
|
||||
var len = html.count
|
||||
|
||||
while (i < result.count) {
|
||||
if (matchTagName_(result, i, "img")) {
|
||||
var end = i
|
||||
while (end < result.count && result[end] != ">") {
|
||||
end = end + 1
|
||||
while (pos < len) {
|
||||
var tagStart = indexOfFrom_(html, "<", pos)
|
||||
if (tagStart < 0) {
|
||||
parts.add(html[pos..-1])
|
||||
break
|
||||
}
|
||||
|
||||
if (matchTagName_(html, tagStart, "img")) {
|
||||
if (tagStart > pos) {
|
||||
parts.add(html[pos...tagStart])
|
||||
}
|
||||
var end = indexOfFrom_(html, ">", tagStart)
|
||||
if (end < 0) end = len - 1
|
||||
|
||||
var tagContent = result[i...end + 1]
|
||||
var tagContent = html[tagStart...end + 1]
|
||||
var src = extractAttr_(tagContent, "src")
|
||||
var alt = extractAttr_(tagContent, "alt")
|
||||
|
||||
if (src == null) src = ""
|
||||
if (alt == null) alt = ""
|
||||
|
||||
var mdImg = ""
|
||||
result = result[0...i] + mdImg + result[end + 1..-1]
|
||||
i = i + mdImg.count
|
||||
parts.add("
|
||||
parts.add(src)
|
||||
parts.add(")")
|
||||
pos = end + 1
|
||||
} else {
|
||||
i = i + 1
|
||||
parts.add(html[pos...tagStart + 1])
|
||||
pos = tagStart + 1
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
return parts.join("")
|
||||
}
|
||||
|
||||
static extractAttr_(tag, name) {
|
||||
@ -1329,42 +1434,49 @@ class Markdown {
|
||||
}
|
||||
|
||||
static replaceTag_(html, tag, prefix, suffix) {
|
||||
var result = html
|
||||
var parts = []
|
||||
var pos = 0
|
||||
var len = html.count
|
||||
|
||||
while (true) {
|
||||
var start = findTagStart_(result, tag)
|
||||
if (start < 0) break
|
||||
|
||||
var openEnd = start
|
||||
while (openEnd < result.count && result[openEnd] != ">") {
|
||||
openEnd = openEnd + 1
|
||||
while (pos < len) {
|
||||
var start = findTagStartFrom_(html, pos, tag)
|
||||
if (start < 0) {
|
||||
parts.add(html[pos..-1])
|
||||
break
|
||||
}
|
||||
if (openEnd >= result.count) break
|
||||
|
||||
if (result[openEnd - 1] == "/") {
|
||||
result = result[0...start] + result[openEnd + 1..-1]
|
||||
if (start > pos) {
|
||||
parts.add(html[pos...start])
|
||||
}
|
||||
|
||||
var openEnd = indexOfFrom_(html, ">", start)
|
||||
if (openEnd < 0) {
|
||||
parts.add(html[pos..-1])
|
||||
break
|
||||
}
|
||||
|
||||
if (html[openEnd - 1] == "/") {
|
||||
pos = openEnd + 1
|
||||
continue
|
||||
}
|
||||
|
||||
var closeStart = findCloseTag_(result, openEnd + 1, tag)
|
||||
var closeStart = findCloseTagFast_(html, openEnd + 1, tag)
|
||||
if (closeStart < 0) {
|
||||
result = result[0...start] + result[openEnd + 1..-1]
|
||||
pos = openEnd + 1
|
||||
continue
|
||||
}
|
||||
|
||||
var content = result[openEnd + 1...closeStart]
|
||||
var content = html[openEnd + 1...closeStart]
|
||||
var closeEnd = indexOfFrom_(html, ">", closeStart)
|
||||
if (closeEnd < 0) closeEnd = len - 1
|
||||
|
||||
var closeEnd = closeStart
|
||||
while (closeEnd < result.count && result[closeEnd] != ">") {
|
||||
closeEnd = closeEnd + 1
|
||||
}
|
||||
|
||||
var before = result[0...start]
|
||||
var after = result[closeEnd + 1..-1]
|
||||
result = before + prefix + content + suffix + after
|
||||
parts.add(prefix)
|
||||
parts.add(content)
|
||||
parts.add(suffix)
|
||||
pos = closeEnd + 1
|
||||
}
|
||||
|
||||
return result
|
||||
return parts.join("")
|
||||
}
|
||||
|
||||
static findTagStart_(html, tag) {
|
||||
@ -1373,30 +1485,36 @@ class Markdown {
|
||||
|
||||
static stripAllTags_(html) {
|
||||
var parts = []
|
||||
var inTag = false
|
||||
for (c in html) {
|
||||
if (c == "<") {
|
||||
inTag = true
|
||||
} else if (c == ">") {
|
||||
inTag = false
|
||||
} else if (!inTag) {
|
||||
parts.add(c)
|
||||
var pos = 0
|
||||
var len = html.count
|
||||
while (pos < len) {
|
||||
var tagStart = indexOfFrom_(html, "<", pos)
|
||||
if (tagStart < 0) {
|
||||
parts.add(html[pos..-1])
|
||||
break
|
||||
}
|
||||
if (tagStart > pos) {
|
||||
parts.add(html[pos...tagStart])
|
||||
}
|
||||
var tagEnd = indexOfFrom_(html, ">", tagStart)
|
||||
if (tagEnd < 0) {
|
||||
break
|
||||
}
|
||||
pos = tagEnd + 1
|
||||
}
|
||||
return parts.join("")
|
||||
}
|
||||
|
||||
static cleanupWhitespace_(text) {
|
||||
var result = text
|
||||
|
||||
while (result.contains("\n\n\n")) {
|
||||
result = result.replace("\n\n\n", "\n\n")
|
||||
}
|
||||
|
||||
var lines = result.split("\n")
|
||||
var lines = text.split("\n")
|
||||
var cleaned = []
|
||||
var prevEmpty = false
|
||||
for (line in lines) {
|
||||
cleaned.add(line.trim())
|
||||
var trimmed = line.trim()
|
||||
var isEmpty = trimmed.count == 0
|
||||
if (isEmpty && prevEmpty) continue
|
||||
cleaned.add(trimmed)
|
||||
prevEmpty = isEmpty
|
||||
}
|
||||
|
||||
return cleaned.join("\n")
|
||||
|
||||
@ -649,111 +649,181 @@ static const char* markdownModuleSource =
|
||||
" static fromHtml(html) { fromHtml(html, {}) }\n"
|
||||
"\n"
|
||||
" static fromHtml(html, options) {\n"
|
||||
" var stripUnknown = options.containsKey(\"stripUnknown\") ? options[\"stripUnknown\"] : true\n"
|
||||
" var text = html\n"
|
||||
"\n"
|
||||
" text = removeHiddenTags_(text)\n"
|
||||
" var text = removeHiddenTagsFast_(html)\n"
|
||||
" text = processBlockElements_(text)\n"
|
||||
" text = processInlineFromHtml_(text)\n"
|
||||
" text = Html.unquote(text)\n"
|
||||
" text = cleanupWhitespace_(text)\n"
|
||||
"\n"
|
||||
" return text.trim()\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static removeHiddenTags_(html) {\n"
|
||||
" var result = html\n"
|
||||
" var tags = [\"script\", \"style\", \"head\", \"meta\", \"link\", \"noscript\"]\n"
|
||||
" for (tag in tags) {\n"
|
||||
" result = removeTagWithContent_(result, tag)\n"
|
||||
" }\n"
|
||||
" return result\n"
|
||||
" }\n"
|
||||
" static removeHiddenTagsFast_(html) {\n"
|
||||
" var hiddenTags = {\"script\": true, \"style\": true, \"head\": true, \"meta\": true, \"link\": true, \"noscript\": true}\n"
|
||||
" var parts = []\n"
|
||||
" var pos = 0\n"
|
||||
" var len = html.count\n"
|
||||
"\n"
|
||||
" static removeTagWithContent_(html, tag) {\n"
|
||||
" var result = html\n"
|
||||
" var openTag = \"<\" + tag\n"
|
||||
" var closeTag = \"</\" + tag + \">\"\n"
|
||||
" while (true) {\n"
|
||||
" var startLower = result.indexOf(openTag)\n"
|
||||
" var startUpper = result.indexOf(\"<\" + tag[0].codePoints.toList[0].toString)\n"
|
||||
" var start = -1\n"
|
||||
" while (pos < len) {\n"
|
||||
" var tagStart = indexOfFrom_(html, \"<\", pos)\n"
|
||||
" if (tagStart < 0) {\n"
|
||||
" parts.add(html[pos..-1])\n"
|
||||
" break\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" var i = 0\n"
|
||||
" while (i < result.count - openTag.count) {\n"
|
||||
" if (matchTagName_(result, i, tag)) {\n"
|
||||
" start = i\n"
|
||||
" break\n"
|
||||
" var tagInfo = parseTagFast_(html, tagStart)\n"
|
||||
" var tagName = tagInfo[\"name\"]\n"
|
||||
" var tagEnd = tagInfo[\"end\"]\n"
|
||||
" var isClose = tagInfo[\"isClose\"]\n"
|
||||
" var isSelfClose = tagInfo[\"isSelfClose\"]\n"
|
||||
"\n"
|
||||
" if (hiddenTags.containsKey(tagName)) {\n"
|
||||
" if (tagStart > pos) {\n"
|
||||
" parts.add(html[pos...tagStart])\n"
|
||||
" }\n"
|
||||
" if (isSelfClose || isClose) {\n"
|
||||
" pos = tagEnd\n"
|
||||
" continue\n"
|
||||
" }\n"
|
||||
" var closePos = findCloseTagFast_(html, tagEnd, tagName)\n"
|
||||
" if (closePos < 0) {\n"
|
||||
" pos = tagEnd\n"
|
||||
" } else {\n"
|
||||
" var closeEnd = indexOfFrom_(html, \">\", closePos)\n"
|
||||
" pos = (closeEnd >= 0) ? closeEnd + 1 : closePos\n"
|
||||
" }\n"
|
||||
" i = i + 1\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" if (start < 0) break\n"
|
||||
"\n"
|
||||
" var tagEnd = start\n"
|
||||
" while (tagEnd < result.count && result[tagEnd] != \">\") {\n"
|
||||
" tagEnd = tagEnd + 1\n"
|
||||
" }\n"
|
||||
" if (tagEnd >= result.count) break\n"
|
||||
"\n"
|
||||
" if (result[tagEnd - 1] == \"/\") {\n"
|
||||
" result = result[0...start] + result[tagEnd + 1..-1]\n"
|
||||
" continue\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" var closeStart = findCloseTag_(result, tagEnd + 1, tag)\n"
|
||||
" if (closeStart < 0) {\n"
|
||||
" result = result[0...start] + result[tagEnd + 1..-1]\n"
|
||||
" } else {\n"
|
||||
" var closeEnd = closeStart\n"
|
||||
" while (closeEnd < result.count && result[closeEnd] != \">\") {\n"
|
||||
" closeEnd = closeEnd + 1\n"
|
||||
" }\n"
|
||||
" result = result[0...start] + result[closeEnd + 1..-1]\n"
|
||||
" parts.add(html[pos...tagEnd])\n"
|
||||
" pos = tagEnd\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" return parts.join(\"\")\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static indexOfFrom_(str, char, start) {\n"
|
||||
" var len = str.count\n"
|
||||
" var i = start\n"
|
||||
" while (i < len) {\n"
|
||||
" if (str[i] == char) return i\n"
|
||||
" i = i + 1\n"
|
||||
" }\n"
|
||||
" return -1\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static parseTagFast_(html, pos) {\n"
|
||||
" var len = html.count\n"
|
||||
" var result = {\"name\": \"\", \"end\": pos + 1, \"isClose\": false, \"isSelfClose\": false}\n"
|
||||
" if (pos >= len || html[pos] != \"<\") return result\n"
|
||||
"\n"
|
||||
" var i = pos + 1\n"
|
||||
" while (i < len && (html[i] == \" \" || html[i] == \"\\t\" || html[i] == \"\\n\")) {\n"
|
||||
" i = i + 1\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" var isClose = false\n"
|
||||
" if (i < len && html[i] == \"/\") {\n"
|
||||
" isClose = true\n"
|
||||
" i = i + 1\n"
|
||||
" while (i < len && (html[i] == \" \" || html[i] == \"\\t\" || html[i] == \"\\n\")) {\n"
|
||||
" i = i + 1\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" var nameStart = i\n"
|
||||
" while (i < len) {\n"
|
||||
" var c = html[i]\n"
|
||||
" if (c == \" \" || c == \">\" || c == \"/\" || c == \"\\t\" || c == \"\\n\") break\n"
|
||||
" i = i + 1\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" var name = \"\"\n"
|
||||
" if (i > nameStart) {\n"
|
||||
" name = toLower_(html[nameStart...i])\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" var tagEnd = indexOfFrom_(html, \">\", i)\n"
|
||||
" if (tagEnd < 0) tagEnd = len\n"
|
||||
" var isSelfClose = tagEnd > 0 && html[tagEnd - 1] == \"/\"\n"
|
||||
"\n"
|
||||
" result[\"name\"] = name\n"
|
||||
" result[\"end\"] = tagEnd + 1\n"
|
||||
" result[\"isClose\"] = isClose\n"
|
||||
" result[\"isSelfClose\"] = isSelfClose\n"
|
||||
" return result\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static toLower_(str) {\n"
|
||||
" var parts = []\n"
|
||||
" for (c in str) {\n"
|
||||
" var code = c.bytes[0]\n"
|
||||
" if (code >= 65 && code <= 90) {\n"
|
||||
" parts.add(String.fromCodePoint(code + 32))\n"
|
||||
" } else {\n"
|
||||
" parts.add(c)\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" return parts.join(\"\")\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static findCloseTagFast_(html, start, tag) {\n"
|
||||
" var len = html.count\n"
|
||||
" var tagLen = tag.count\n"
|
||||
" var i = start\n"
|
||||
" while (i < len - tagLen - 2) {\n"
|
||||
" var idx = indexOfFrom_(html, \"<\", i)\n"
|
||||
" if (idx < 0) return -1\n"
|
||||
" if (idx + 1 < len && html[idx + 1] == \"/\") {\n"
|
||||
" var match = true\n"
|
||||
" var j = 0\n"
|
||||
" while (j < tagLen && idx + 2 + j < len) {\n"
|
||||
" var c1 = html[idx + 2 + j].bytes[0]\n"
|
||||
" var c2 = tag[j].bytes[0]\n"
|
||||
" if (c1 >= 65 && c1 <= 90) c1 = c1 + 32\n"
|
||||
" if (c1 != c2) {\n"
|
||||
" match = false\n"
|
||||
" break\n"
|
||||
" }\n"
|
||||
" j = j + 1\n"
|
||||
" }\n"
|
||||
" if (match && j == tagLen) {\n"
|
||||
" var next = idx + 2 + tagLen\n"
|
||||
" if (next >= len || html[next] == \">\" || html[next] == \" \" || html[next] == \"\\t\" || html[next] == \"\\n\") {\n"
|
||||
" return idx\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" i = idx + 1\n"
|
||||
" }\n"
|
||||
" return -1\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static matchTagName_(html, pos, tag) {\n"
|
||||
" if (pos >= html.count || html[pos] != \"<\") return false\n"
|
||||
" var rest = html[pos + 1..-1].trim()\n"
|
||||
" var tagLower = tag\n"
|
||||
" for (i in 0...tag.count) {\n"
|
||||
" if (i >= rest.count) return false\n"
|
||||
" var c1 = rest[i].codePoints.toList[0]\n"
|
||||
" var c2 = tag[i].codePoints.toList[0]\n"
|
||||
" var len = html.count\n"
|
||||
" var tagLen = tag.count\n"
|
||||
" if (pos >= len || html[pos] != \"<\") return false\n"
|
||||
" var i = pos + 1\n"
|
||||
" while (i < len && (html[i] == \" \" || html[i] == \"\\t\" || html[i] == \"\\n\")) {\n"
|
||||
" i = i + 1\n"
|
||||
" }\n"
|
||||
" for (j in 0...tagLen) {\n"
|
||||
" if (i + j >= len) return false\n"
|
||||
" var c1 = html[i + j].bytes[0]\n"
|
||||
" var c2 = tag[j].bytes[0]\n"
|
||||
" if (c1 >= 65 && c1 <= 90) c1 = c1 + 32\n"
|
||||
" if (c2 >= 65 && c2 <= 90) c2 = c2 + 32\n"
|
||||
" if (c1 != c2) return false\n"
|
||||
" }\n"
|
||||
" if (tag.count < rest.count) {\n"
|
||||
" var next = rest[tag.count]\n"
|
||||
" var nextPos = i + tagLen\n"
|
||||
" if (nextPos < len) {\n"
|
||||
" var next = html[nextPos]\n"
|
||||
" if (next != \" \" && next != \">\" && next != \"/\" && next != \"\\t\" && next != \"\\n\") return false\n"
|
||||
" }\n"
|
||||
" return true\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static findCloseTag_(html, start, tag) {\n"
|
||||
" var i = start\n"
|
||||
" while (i < html.count - tag.count - 2) {\n"
|
||||
" if (html[i] == \"<\" && html[i + 1] == \"/\") {\n"
|
||||
" var match = true\n"
|
||||
" for (j in 0...tag.count) {\n"
|
||||
" var c1 = html[i + 2 + j].codePoints.toList[0]\n"
|
||||
" var c2 = tag[j].codePoints.toList[0]\n"
|
||||
" if (c1 >= 65 && c1 <= 90) c1 = c1 + 32\n"
|
||||
" if (c2 >= 65 && c2 <= 90) c2 = c2 + 32\n"
|
||||
" if (c1 != c2) {\n"
|
||||
" match = false\n"
|
||||
" break\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" if (match) return i\n"
|
||||
" }\n"
|
||||
" i = i + 1\n"
|
||||
" }\n"
|
||||
" return -1\n"
|
||||
" return findCloseTagFast_(html, start, tag)\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static processBlockElements_(html) {\n"
|
||||
@ -1146,52 +1216,63 @@ static const char* markdownModuleSource =
|
||||
"\n"
|
||||
" static findTagStartFrom_(html, start, tag) {\n"
|
||||
" var i = start\n"
|
||||
" while (i < html.count) {\n"
|
||||
" if (matchTagName_(html, i, tag)) return i\n"
|
||||
" i = i + 1\n"
|
||||
" var len = html.count\n"
|
||||
" while (i < len) {\n"
|
||||
" var idx = indexOfFrom_(html, \"<\", i)\n"
|
||||
" if (idx < 0) return -1\n"
|
||||
" if (matchTagName_(html, idx, tag)) return idx\n"
|
||||
" i = idx + 1\n"
|
||||
" }\n"
|
||||
" return -1\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static processHrFromHtml_(html) {\n"
|
||||
" var parts = []\n"
|
||||
" var i = 0\n"
|
||||
" while (i < html.count) {\n"
|
||||
" if (matchTagName_(html, i, \"hr\")) {\n"
|
||||
" var end = i\n"
|
||||
" while (end < html.count && html[end] != \">\") {\n"
|
||||
" end = end + 1\n"
|
||||
" }\n"
|
||||
" var pos = 0\n"
|
||||
" var len = html.count\n"
|
||||
" while (pos < len) {\n"
|
||||
" var tagStart = indexOfFrom_(html, \"<\", pos)\n"
|
||||
" if (tagStart < 0) {\n"
|
||||
" parts.add(html[pos..-1])\n"
|
||||
" break\n"
|
||||
" }\n"
|
||||
" if (matchTagName_(html, tagStart, \"hr\")) {\n"
|
||||
" if (tagStart > pos) parts.add(html[pos...tagStart])\n"
|
||||
" var end = indexOfFrom_(html, \">\", tagStart)\n"
|
||||
" if (end < 0) end = len - 1\n"
|
||||
" parts.add(\"\\n---\\n\")\n"
|
||||
" i = end + 1\n"
|
||||
" pos = end + 1\n"
|
||||
" } else {\n"
|
||||
" parts.add(html[i])\n"
|
||||
" i = i + 1\n"
|
||||
" parts.add(html[pos...tagStart + 1])\n"
|
||||
" pos = tagStart + 1\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" return parts.join(\"\")\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static processParagraphsFromHtml_(html) {\n"
|
||||
" var result = html\n"
|
||||
" result = replaceTag_(result, \"p\", \"\", \"\\n\\n\")\n"
|
||||
" return result\n"
|
||||
" return replaceTag_(html, \"p\", \"\", \"\\n\\n\")\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static processBrFromHtml_(html) {\n"
|
||||
" var parts = []\n"
|
||||
" var i = 0\n"
|
||||
" while (i < html.count) {\n"
|
||||
" if (matchTagName_(html, i, \"br\")) {\n"
|
||||
" var end = i\n"
|
||||
" while (end < html.count && html[end] != \">\") {\n"
|
||||
" end = end + 1\n"
|
||||
" }\n"
|
||||
" var pos = 0\n"
|
||||
" var len = html.count\n"
|
||||
" while (pos < len) {\n"
|
||||
" var tagStart = indexOfFrom_(html, \"<\", pos)\n"
|
||||
" if (tagStart < 0) {\n"
|
||||
" parts.add(html[pos..-1])\n"
|
||||
" break\n"
|
||||
" }\n"
|
||||
" if (matchTagName_(html, tagStart, \"br\")) {\n"
|
||||
" if (tagStart > pos) parts.add(html[pos...tagStart])\n"
|
||||
" var end = indexOfFrom_(html, \">\", tagStart)\n"
|
||||
" if (end < 0) end = len - 1\n"
|
||||
" parts.add(\"\\n\")\n"
|
||||
" i = end + 1\n"
|
||||
" pos = end + 1\n"
|
||||
" } else {\n"
|
||||
" parts.add(html[i])\n"
|
||||
" i = i + 1\n"
|
||||
" parts.add(html[pos...tagStart + 1])\n"
|
||||
" pos = tagStart + 1\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" return parts.join(\"\")\n"
|
||||
@ -1246,71 +1327,95 @@ static const char* markdownModuleSource =
|
||||
" }\n"
|
||||
"\n"
|
||||
" static processLinksFromHtml_(html) {\n"
|
||||
" var result = html\n"
|
||||
" var parts = []\n"
|
||||
" var pos = 0\n"
|
||||
" var len = html.count\n"
|
||||
"\n"
|
||||
" while (true) {\n"
|
||||
" var start = findTagStart_(result, \"a\")\n"
|
||||
" if (start < 0) break\n"
|
||||
"\n"
|
||||
" var openEnd = start\n"
|
||||
" while (openEnd < result.count && result[openEnd] != \">\") {\n"
|
||||
" openEnd = openEnd + 1\n"
|
||||
" while (pos < len) {\n"
|
||||
" var start = findTagStartFrom_(html, pos, \"a\")\n"
|
||||
" if (start < 0) {\n"
|
||||
" parts.add(html[pos..-1])\n"
|
||||
" break\n"
|
||||
" }\n"
|
||||
" if (openEnd >= result.count) break\n"
|
||||
"\n"
|
||||
" var tagContent = result[start...openEnd + 1]\n"
|
||||
" if (start > pos) {\n"
|
||||
" parts.add(html[pos...start])\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" var openEnd = indexOfFrom_(html, \">\", start)\n"
|
||||
" if (openEnd < 0) {\n"
|
||||
" parts.add(html[pos..-1])\n"
|
||||
" break\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" var tagContent = html[start...openEnd + 1]\n"
|
||||
" var href = extractAttr_(tagContent, \"href\")\n"
|
||||
"\n"
|
||||
" var closeStart = findCloseTag_(result, openEnd + 1, \"a\")\n"
|
||||
" if (closeStart < 0) break\n"
|
||||
"\n"
|
||||
" var linkText = result[openEnd + 1...closeStart]\n"
|
||||
"\n"
|
||||
" var closeEnd = closeStart\n"
|
||||
" while (closeEnd < result.count && result[closeEnd] != \">\") {\n"
|
||||
" closeEnd = closeEnd + 1\n"
|
||||
" var closeStart = findCloseTagFast_(html, openEnd + 1, \"a\")\n"
|
||||
" if (closeStart < 0) {\n"
|
||||
" parts.add(html[pos...openEnd + 1])\n"
|
||||
" pos = openEnd + 1\n"
|
||||
" continue\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" var before = result[0...start]\n"
|
||||
" var after = result[closeEnd + 1..-1]\n"
|
||||
" var linkText = html[openEnd + 1...closeStart]\n"
|
||||
" var closeEnd = indexOfFrom_(html, \">\", closeStart)\n"
|
||||
" if (closeEnd < 0) closeEnd = len - 1\n"
|
||||
"\n"
|
||||
" if (href != null && href.count > 0) {\n"
|
||||
" result = before + \"[\" + stripAllTags_(linkText) + \"](\" + href + \")\" + after\n"
|
||||
" parts.add(\"[\")\n"
|
||||
" parts.add(stripAllTags_(linkText))\n"
|
||||
" parts.add(\"](\")\n"
|
||||
" parts.add(href)\n"
|
||||
" parts.add(\")\")\n"
|
||||
" } else {\n"
|
||||
" result = before + stripAllTags_(linkText) + after\n"
|
||||
" parts.add(stripAllTags_(linkText))\n"
|
||||
" }\n"
|
||||
" pos = closeEnd + 1\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" return result\n"
|
||||
" return parts.join(\"\")\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static processImagesFromHtml_(html) {\n"
|
||||
" var result = html\n"
|
||||
" var i = 0\n"
|
||||
" var parts = []\n"
|
||||
" var pos = 0\n"
|
||||
" var len = html.count\n"
|
||||
"\n"
|
||||
" while (i < result.count) {\n"
|
||||
" if (matchTagName_(result, i, \"img\")) {\n"
|
||||
" var end = i\n"
|
||||
" while (end < result.count && result[end] != \">\") {\n"
|
||||
" end = end + 1\n"
|
||||
" while (pos < len) {\n"
|
||||
" var tagStart = indexOfFrom_(html, \"<\", pos)\n"
|
||||
" if (tagStart < 0) {\n"
|
||||
" parts.add(html[pos..-1])\n"
|
||||
" break\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" if (matchTagName_(html, tagStart, \"img\")) {\n"
|
||||
" if (tagStart > pos) {\n"
|
||||
" parts.add(html[pos...tagStart])\n"
|
||||
" }\n"
|
||||
" var end = indexOfFrom_(html, \">\", tagStart)\n"
|
||||
" if (end < 0) end = len - 1\n"
|
||||
"\n"
|
||||
" var tagContent = result[i...end + 1]\n"
|
||||
" var tagContent = html[tagStart...end + 1]\n"
|
||||
" var src = extractAttr_(tagContent, \"src\")\n"
|
||||
" var alt = extractAttr_(tagContent, \"alt\")\n"
|
||||
"\n"
|
||||
" if (src == null) src = \"\"\n"
|
||||
" if (alt == null) alt = \"\"\n"
|
||||
"\n"
|
||||
" var mdImg = \"\"\n"
|
||||
" result = result[0...i] + mdImg + result[end + 1..-1]\n"
|
||||
" i = i + mdImg.count\n"
|
||||
" parts.add(\"\n"
|
||||
" parts.add(src)\n"
|
||||
" parts.add(\")\")\n"
|
||||
" pos = end + 1\n"
|
||||
" } else {\n"
|
||||
" i = i + 1\n"
|
||||
" parts.add(html[pos...tagStart + 1])\n"
|
||||
" pos = tagStart + 1\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" return result\n"
|
||||
" return parts.join(\"\")\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static extractAttr_(tag, name) {\n"
|
||||
@ -1333,42 +1438,49 @@ static const char* markdownModuleSource =
|
||||
" }\n"
|
||||
"\n"
|
||||
" static replaceTag_(html, tag, prefix, suffix) {\n"
|
||||
" var result = html\n"
|
||||
" var parts = []\n"
|
||||
" var pos = 0\n"
|
||||
" var len = html.count\n"
|
||||
"\n"
|
||||
" while (true) {\n"
|
||||
" var start = findTagStart_(result, tag)\n"
|
||||
" if (start < 0) break\n"
|
||||
"\n"
|
||||
" var openEnd = start\n"
|
||||
" while (openEnd < result.count && result[openEnd] != \">\") {\n"
|
||||
" openEnd = openEnd + 1\n"
|
||||
" while (pos < len) {\n"
|
||||
" var start = findTagStartFrom_(html, pos, tag)\n"
|
||||
" if (start < 0) {\n"
|
||||
" parts.add(html[pos..-1])\n"
|
||||
" break\n"
|
||||
" }\n"
|
||||
" if (openEnd >= result.count) break\n"
|
||||
"\n"
|
||||
" if (result[openEnd - 1] == \"/\") {\n"
|
||||
" result = result[0...start] + result[openEnd + 1..-1]\n"
|
||||
" if (start > pos) {\n"
|
||||
" parts.add(html[pos...start])\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" var openEnd = indexOfFrom_(html, \">\", start)\n"
|
||||
" if (openEnd < 0) {\n"
|
||||
" parts.add(html[pos..-1])\n"
|
||||
" break\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" if (html[openEnd - 1] == \"/\") {\n"
|
||||
" pos = openEnd + 1\n"
|
||||
" continue\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" var closeStart = findCloseTag_(result, openEnd + 1, tag)\n"
|
||||
" var closeStart = findCloseTagFast_(html, openEnd + 1, tag)\n"
|
||||
" if (closeStart < 0) {\n"
|
||||
" result = result[0...start] + result[openEnd + 1..-1]\n"
|
||||
" pos = openEnd + 1\n"
|
||||
" continue\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" var content = result[openEnd + 1...closeStart]\n"
|
||||
" var content = html[openEnd + 1...closeStart]\n"
|
||||
" var closeEnd = indexOfFrom_(html, \">\", closeStart)\n"
|
||||
" if (closeEnd < 0) closeEnd = len - 1\n"
|
||||
"\n"
|
||||
" var closeEnd = closeStart\n"
|
||||
" while (closeEnd < result.count && result[closeEnd] != \">\") {\n"
|
||||
" closeEnd = closeEnd + 1\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" var before = result[0...start]\n"
|
||||
" var after = result[closeEnd + 1..-1]\n"
|
||||
" result = before + prefix + content + suffix + after\n"
|
||||
" parts.add(prefix)\n"
|
||||
" parts.add(content)\n"
|
||||
" parts.add(suffix)\n"
|
||||
" pos = closeEnd + 1\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" return result\n"
|
||||
" return parts.join(\"\")\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static findTagStart_(html, tag) {\n"
|
||||
@ -1377,30 +1489,36 @@ static const char* markdownModuleSource =
|
||||
"\n"
|
||||
" static stripAllTags_(html) {\n"
|
||||
" var parts = []\n"
|
||||
" var inTag = false\n"
|
||||
" for (c in html) {\n"
|
||||
" if (c == \"<\") {\n"
|
||||
" inTag = true\n"
|
||||
" } else if (c == \">\") {\n"
|
||||
" inTag = false\n"
|
||||
" } else if (!inTag) {\n"
|
||||
" parts.add(c)\n"
|
||||
" var pos = 0\n"
|
||||
" var len = html.count\n"
|
||||
" while (pos < len) {\n"
|
||||
" var tagStart = indexOfFrom_(html, \"<\", pos)\n"
|
||||
" if (tagStart < 0) {\n"
|
||||
" parts.add(html[pos..-1])\n"
|
||||
" break\n"
|
||||
" }\n"
|
||||
" if (tagStart > pos) {\n"
|
||||
" parts.add(html[pos...tagStart])\n"
|
||||
" }\n"
|
||||
" var tagEnd = indexOfFrom_(html, \">\", tagStart)\n"
|
||||
" if (tagEnd < 0) {\n"
|
||||
" break\n"
|
||||
" }\n"
|
||||
" pos = tagEnd + 1\n"
|
||||
" }\n"
|
||||
" return parts.join(\"\")\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static cleanupWhitespace_(text) {\n"
|
||||
" var result = text\n"
|
||||
"\n"
|
||||
" while (result.contains(\"\\n\\n\\n\")) {\n"
|
||||
" result = result.replace(\"\\n\\n\\n\", \"\\n\\n\")\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" var lines = result.split(\"\\n\")\n"
|
||||
" var lines = text.split(\"\\n\")\n"
|
||||
" var cleaned = []\n"
|
||||
" var prevEmpty = false\n"
|
||||
" for (line in lines) {\n"
|
||||
" cleaned.add(line.trim())\n"
|
||||
" var trimmed = line.trim()\n"
|
||||
" var isEmpty = trimmed.count == 0\n"
|
||||
" if (isEmpty && prevEmpty) continue\n"
|
||||
" cleaned.add(trimmed)\n"
|
||||
" prevEmpty = isEmpty\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" return cleaned.join(\"\\n\")\n"
|
||||
|
||||
24
test/html/document_createelement.wren
vendored
Normal file
24
test/html/document_createelement.wren
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id='container'></div>")
|
||||
|
||||
var p = doc.createElement("p")
|
||||
System.print(p != null) // expect: true
|
||||
System.print(p.tagName) // expect: P
|
||||
|
||||
var span = doc.createElement("span")
|
||||
System.print(span.tagName) // expect: SPAN
|
||||
|
||||
var div = doc.createElement("div")
|
||||
System.print(div.tagName) // expect: DIV
|
||||
|
||||
p.textContent = "New paragraph"
|
||||
System.print(p.textContent) // expect: New paragraph
|
||||
|
||||
var container = doc.getElementById("container")
|
||||
container.appendChild(p)
|
||||
System.print(container.children.count) // expect: 1
|
||||
System.print(container.firstChild.tagName) // expect: P
|
||||
|
||||
21
test/html/document_createtextnode.wren
vendored
Normal file
21
test/html/document_createtextnode.wren
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id='container'></div>")
|
||||
|
||||
var text = doc.createTextNode("Hello World")
|
||||
System.print(text != null) // expect: true
|
||||
System.print(text.textContent) // expect: Hello World
|
||||
System.print(text.nodeType) // expect: 3
|
||||
|
||||
var empty = doc.createTextNode("")
|
||||
System.print(empty.textContent) // expect:
|
||||
|
||||
var special = doc.createTextNode("Line1\nLine2")
|
||||
System.print(special.textContent.contains("\n")) // expect: true
|
||||
|
||||
var container = doc.getElementById("container")
|
||||
container.appendChild(text)
|
||||
System.print(container.textContent) // expect: Hello World
|
||||
|
||||
22
test/html/document_getelementbyid.wren
vendored
Normal file
22
test/html/document_getelementbyid.wren
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id='first'>One</div><div id='second'>Two</div><div>No ID</div>")
|
||||
|
||||
var first = doc.getElementById("first")
|
||||
System.print(first != null) // expect: true
|
||||
System.print(first.textContent) // expect: One
|
||||
|
||||
var second = doc.getElementById("second")
|
||||
System.print(second != null) // expect: true
|
||||
System.print(second.textContent) // expect: Two
|
||||
|
||||
var notFound = doc.getElementById("nonexistent")
|
||||
System.print(notFound == null) // expect: true
|
||||
|
||||
var nested = Document.parse("<div><span id='inner'>Nested</span></div>")
|
||||
var inner = nested.getElementById("inner")
|
||||
System.print(inner != null) // expect: true
|
||||
System.print(inner.tagName) // expect: SPAN
|
||||
|
||||
25
test/html/document_getelementsbyclassname.wren
vendored
Normal file
25
test/html/document_getelementsbyclassname.wren
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div class='box'>A</div><div class='box'>B</div><div class='other'>C</div>")
|
||||
|
||||
var boxes = doc.getElementsByClassName("box")
|
||||
System.print(boxes.count) // expect: 2
|
||||
System.print(boxes[0].textContent) // expect: A
|
||||
System.print(boxes[1].textContent) // expect: B
|
||||
|
||||
var other = doc.getElementsByClassName("other")
|
||||
System.print(other.count) // expect: 1
|
||||
System.print(other[0].textContent) // expect: C
|
||||
|
||||
var none = doc.getElementsByClassName("nonexistent")
|
||||
System.print(none.count) // expect: 0
|
||||
|
||||
var multi = Document.parse("<div class='a b'>Multi</div><div class='a'>Single</div>")
|
||||
var aClass = multi.getElementsByClassName("a")
|
||||
System.print(aClass.count) // expect: 2
|
||||
|
||||
var bClass = multi.getElementsByClassName("b")
|
||||
System.print(bClass.count) // expect: 1
|
||||
|
||||
24
test/html/document_getelementsbytagname.wren
vendored
Normal file
24
test/html/document_getelementsbytagname.wren
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div><p>One</p><p>Two</p><span>Three</span></div>")
|
||||
|
||||
var paragraphs = doc.getElementsByTagName("p")
|
||||
System.print(paragraphs.count) // expect: 2
|
||||
System.print(paragraphs[0].textContent) // expect: One
|
||||
System.print(paragraphs[1].textContent) // expect: Two
|
||||
|
||||
var spans = doc.getElementsByTagName("span")
|
||||
System.print(spans.count) // expect: 1
|
||||
System.print(spans[0].textContent) // expect: Three
|
||||
|
||||
var divs = doc.getElementsByTagName("div")
|
||||
System.print(divs.count) // expect: 1
|
||||
|
||||
var none = doc.getElementsByTagName("article")
|
||||
System.print(none.count) // expect: 0
|
||||
|
||||
var caseInsensitive = doc.getElementsByTagName("P")
|
||||
System.print(caseInsensitive.count) // expect: 2
|
||||
|
||||
18
test/html/document_parse.wren
vendored
Normal file
18
test/html/document_parse.wren
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div>Hello</div>")
|
||||
System.print(doc != null) // expect: true
|
||||
|
||||
var doc2 = Document.parse("<html><head></head><body></body></html>")
|
||||
System.print(doc2.documentElement != null) // expect: true
|
||||
|
||||
var doc3 = Document.parse("")
|
||||
System.print(doc3 != null) // expect: true
|
||||
|
||||
var doc4 = Document.parse("<p>Text</p>")
|
||||
System.print(doc4.querySelector("p").textContent) // expect: Text
|
||||
|
||||
var doc5 = Document.parse("<div><span><a>Link</a></span></div>")
|
||||
System.print(doc5.querySelector("a").textContent) // expect: Link
|
||||
26
test/html/document_properties.wren
vendored
Normal file
26
test/html/document_properties.wren
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<html><head><title>Test Title</title></head><body><div>Content</div></body></html>")
|
||||
|
||||
System.print(doc.documentElement != null) // expect: true
|
||||
System.print(doc.documentElement.tagName) // expect: HTML
|
||||
|
||||
System.print(doc.head != null) // expect: true
|
||||
System.print(doc.head.tagName) // expect: HEAD
|
||||
|
||||
System.print(doc.body != null) // expect: true
|
||||
System.print(doc.body.tagName) // expect: BODY
|
||||
|
||||
System.print(doc.title) // expect: Test Title
|
||||
|
||||
doc.title = "New Title"
|
||||
System.print(doc.title) // expect: New Title
|
||||
|
||||
var html = doc.outerHTML
|
||||
System.print(html.indexOf("<html") >= 0) // expect: true
|
||||
System.print(html.indexOf("</html>") >= 0) // expect: true
|
||||
|
||||
var inner = doc.innerHTML
|
||||
System.print(inner.indexOf("<head") >= 0) // expect: true
|
||||
29
test/html/document_queryselector.wren
vendored
Normal file
29
test/html/document_queryselector.wren
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id='main' class='container'><p class='text'>Hello</p><span>World</span></div>")
|
||||
|
||||
var byTag = doc.querySelector("p")
|
||||
System.print(byTag != null) // expect: true
|
||||
System.print(byTag.tagName) // expect: P
|
||||
|
||||
var byId = doc.querySelector("#main")
|
||||
System.print(byId != null) // expect: true
|
||||
System.print(byId.tagName) // expect: DIV
|
||||
|
||||
var byClass = doc.querySelector(".text")
|
||||
System.print(byClass != null) // expect: true
|
||||
System.print(byClass.textContent) // expect: Hello
|
||||
|
||||
var byCompound = doc.querySelector("div.container")
|
||||
System.print(byCompound != null) // expect: true
|
||||
|
||||
var notFound = doc.querySelector(".nonexistent")
|
||||
System.print(notFound == null) // expect: true
|
||||
|
||||
var descendant = doc.querySelector("div p")
|
||||
System.print(descendant != null) // expect: true
|
||||
|
||||
var child = doc.querySelector("div > p")
|
||||
System.print(child != null) // expect: true
|
||||
25
test/html/document_queryselectorall.wren
vendored
Normal file
25
test/html/document_queryselectorall.wren
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div><p class='a'>One</p><p class='a'>Two</p><p class='b'>Three</p></div>")
|
||||
|
||||
var all = doc.querySelectorAll("p")
|
||||
System.print(all.count) // expect: 3
|
||||
|
||||
var byClass = doc.querySelectorAll(".a")
|
||||
System.print(byClass.count) // expect: 2
|
||||
System.print(byClass[0].textContent) // expect: One
|
||||
System.print(byClass[1].textContent) // expect: Two
|
||||
|
||||
var none = doc.querySelectorAll(".nonexistent")
|
||||
System.print(none.count) // expect: 0
|
||||
|
||||
var single = doc.querySelectorAll(".b")
|
||||
System.print(single.count) // expect: 1
|
||||
System.print(single[0].textContent) // expect: Three
|
||||
|
||||
var nested = Document.parse("<div><span><a>Link1</a></span><a>Link2</a></div>")
|
||||
var links = nested.querySelectorAll("a")
|
||||
System.print(links.count) // expect: 2
|
||||
|
||||
34
test/html/dom_attributes.wren
vendored
Normal file
34
test/html/dom_attributes.wren
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id=\"test\" class=\"foo bar\" data-value=\"123\"></div>")
|
||||
var div = doc.querySelector("div")
|
||||
|
||||
System.print(div.id) // expect: test
|
||||
System.print(div.className) // expect: foo bar
|
||||
System.print(div.hasAttribute("id")) // expect: true
|
||||
System.print(div.hasAttribute("missing")) // expect: false
|
||||
|
||||
div.setAttribute("title", "My Title")
|
||||
System.print(div.getAttribute("title")) // expect: My Title
|
||||
|
||||
div.removeAttribute("title")
|
||||
System.print(div.getAttribute("title") == null) // expect: true
|
||||
|
||||
var attrs = div.attributes
|
||||
System.print(attrs["id"]) // expect: test
|
||||
|
||||
var classList = div.classList
|
||||
System.print(classList.count) // expect: 2
|
||||
System.print(classList[0]) // expect: foo
|
||||
System.print(classList[1]) // expect: bar
|
||||
|
||||
var dataset = div.dataset
|
||||
System.print(dataset["value"]) // expect: 123
|
||||
|
||||
div.id = "newId"
|
||||
System.print(div.id) // expect: newId
|
||||
|
||||
div.className = "single-class"
|
||||
System.print(div.className) // expect: single-class
|
||||
22
test/html/dom_content.wren
vendored
Normal file
22
test/html/dom_content.wren
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div><p>Hello</p><span>World</span></div>")
|
||||
var div = doc.querySelector("div")
|
||||
|
||||
System.print(div.textContent) // expect: HelloWorld
|
||||
|
||||
div.textContent = "New Content"
|
||||
System.print(div.textContent) // expect: New Content
|
||||
System.print(div.children.count) // expect: 0
|
||||
|
||||
var doc2 = Document.parse("<div id=\"test\"></div>")
|
||||
var div2 = doc2.getElementById("test")
|
||||
div2.innerHTML = "<p>First</p><p>Second</p>"
|
||||
System.print(div2.children.count) // expect: 2
|
||||
System.print(div2.querySelector("p").textContent) // expect: First
|
||||
|
||||
var outer = div2.outerHTML
|
||||
System.print(outer.indexOf("<div") >= 0) // expect: true
|
||||
System.print(outer.indexOf("</div>") >= 0) // expect: true
|
||||
32
test/html/dom_manipulate.wren
vendored
Normal file
32
test/html/dom_manipulate.wren
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id=\"container\"></div>")
|
||||
var container = doc.getElementById("container")
|
||||
|
||||
var p = doc.createElement("p")
|
||||
p.textContent = "Hello World"
|
||||
container.appendChild(p)
|
||||
|
||||
System.print(container.innerHTML) // expect: <p>Hello World</p>
|
||||
|
||||
var span = doc.createElement("span")
|
||||
span.textContent = "Inserted"
|
||||
container.insertBefore(span, p)
|
||||
|
||||
System.print(container.children.count) // expect: 2
|
||||
System.print(container.firstElementChild.tagName) // expect: SPAN
|
||||
|
||||
var removed = container.removeChild(span)
|
||||
System.print(removed.tagName) // expect: SPAN
|
||||
System.print(container.children.count) // expect: 1
|
||||
|
||||
var newP = doc.createElement("p")
|
||||
newP.textContent = "Replaced"
|
||||
container.replaceChild(newP, p)
|
||||
System.print(container.innerHTML) // expect: <p>Replaced</p>
|
||||
|
||||
var clone = newP.cloneNode(true)
|
||||
System.print(clone.textContent) // expect: Replaced
|
||||
System.print(clone.parentNode == null) // expect: true
|
||||
21
test/html/dom_parse.wren
vendored
Normal file
21
test/html/dom_parse.wren
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div><p>Hello</p></div>")
|
||||
System.print(doc.documentElement != null) // expect: true
|
||||
|
||||
var doc2 = Document.parse("<html><head><title>Test</title></head><body><div id=\"main\">Content</div></body></html>")
|
||||
System.print(doc2.head != null) // expect: true
|
||||
System.print(doc2.body != null) // expect: true
|
||||
System.print(doc2.title) // expect: Test
|
||||
|
||||
var doc3 = Document.parse("<div class=\"foo bar\" data-id=\"123\">Text</div>")
|
||||
var div = doc3.querySelector("div")
|
||||
System.print(div.className) // expect: foo bar
|
||||
System.print(div.getAttribute("data-id")) // expect: 123
|
||||
|
||||
var doc4 = Document.parse("<img src=\"test.png\" />")
|
||||
var img = doc4.querySelector("img")
|
||||
System.print(img.tagName) // expect: IMG
|
||||
System.print(img.getAttribute("src")) // expect: test.png
|
||||
34
test/html/dom_query.wren
vendored
Normal file
34
test/html/dom_query.wren
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var html = "<div id=\"root\"><p class=\"text first\">One</p><p class=\"text\">Two</p><span>Three</span></div>"
|
||||
var doc = Document.parse(html)
|
||||
|
||||
var byId = doc.getElementById("root")
|
||||
System.print(byId.tagName) // expect: DIV
|
||||
|
||||
var byTag = doc.querySelector("p")
|
||||
System.print(byTag.textContent) // expect: One
|
||||
|
||||
var byClass = doc.querySelector(".text")
|
||||
System.print(byClass.textContent) // expect: One
|
||||
|
||||
var allP = doc.querySelectorAll("p")
|
||||
System.print(allP.count) // expect: 2
|
||||
|
||||
var allText = doc.getElementsByClassName("text")
|
||||
System.print(allText.count) // expect: 2
|
||||
|
||||
var spans = doc.getElementsByTagName("span")
|
||||
System.print(spans.count) // expect: 1
|
||||
System.print(spans[0].textContent) // expect: Three
|
||||
|
||||
var compound = doc.querySelector("p.text.first")
|
||||
System.print(compound.textContent) // expect: One
|
||||
|
||||
var descendant = doc.querySelector("div p")
|
||||
System.print(descendant != null) // expect: true
|
||||
|
||||
var child = doc.querySelector("div > p")
|
||||
System.print(child != null) // expect: true
|
||||
21
test/html/dom_serialize.wren
vendored
Normal file
21
test/html/dom_serialize.wren
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id=\"test\" class=\"foo\"><p>Content</p></div>")
|
||||
var html = doc.outerHTML
|
||||
|
||||
System.print(html.indexOf("<div") >= 0) // expect: true
|
||||
System.print(html.indexOf("id=\"test\"") >= 0) // expect: true
|
||||
System.print(html.indexOf("class=\"foo\"") >= 0) // expect: true
|
||||
System.print(html.indexOf("<p>Content</p>") >= 0) // expect: true
|
||||
System.print(html.indexOf("</div>") >= 0) // expect: true
|
||||
|
||||
var doc2 = Document.parse("<img src=\"test.png\" />")
|
||||
var imgHtml = doc2.outerHTML
|
||||
System.print(imgHtml.indexOf("<img") >= 0) // expect: true
|
||||
System.print(imgHtml.indexOf("/>") >= 0) // expect: true
|
||||
|
||||
var doc3 = Document.parse("<p>Hello & World</p>")
|
||||
var p = doc3.querySelector("p")
|
||||
System.print(p.textContent) // expect: Hello & World
|
||||
28
test/html/dom_traverse.wren
vendored
Normal file
28
test/html/dom_traverse.wren
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var html = "<div><p>First</p><span>Second</span><p>Third</p></div>"
|
||||
var doc = Document.parse(html)
|
||||
|
||||
var div = doc.querySelector("div")
|
||||
|
||||
System.print(div.hasChildNodes()) // expect: true
|
||||
System.print(div.children.count) // expect: 3
|
||||
|
||||
var first = div.firstElementChild
|
||||
System.print(first.tagName) // expect: P
|
||||
System.print(first.textContent) // expect: First
|
||||
|
||||
var last = div.lastElementChild
|
||||
System.print(last.tagName) // expect: P
|
||||
System.print(last.textContent) // expect: Third
|
||||
|
||||
var span = doc.querySelector("span")
|
||||
System.print(span.previousElementSibling.tagName) // expect: P
|
||||
System.print(span.nextElementSibling.tagName) // expect: P
|
||||
|
||||
System.print(span.parentElement.tagName) // expect: DIV
|
||||
|
||||
var p = doc.querySelector("p")
|
||||
System.print(p.parentNode != null) // expect: true
|
||||
25
test/html/element_appendchild.wren
vendored
Normal file
25
test/html/element_appendchild.wren
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id='container'></div>")
|
||||
|
||||
var container = doc.getElementById("container")
|
||||
System.print(container.children.count) // expect: 0
|
||||
|
||||
var p = doc.createElement("p")
|
||||
p.textContent = "First"
|
||||
container.appendChild(p)
|
||||
System.print(container.children.count) // expect: 1
|
||||
System.print(container.firstChild.textContent) // expect: First
|
||||
|
||||
var span = doc.createElement("span")
|
||||
span.textContent = "Second"
|
||||
container.appendChild(span)
|
||||
System.print(container.children.count) // expect: 2
|
||||
System.print(container.lastChild.textContent) // expect: Second
|
||||
|
||||
var text = doc.createTextNode("Text node")
|
||||
container.appendChild(text)
|
||||
System.print(container.textContent.contains("Text node")) // expect: true
|
||||
|
||||
27
test/html/element_attributes.wren
vendored
Normal file
27
test/html/element_attributes.wren
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<a href='http://example.com' target='_blank'>Link</a>")
|
||||
|
||||
var a = doc.querySelector("a")
|
||||
|
||||
System.print(a.getAttribute("href")) // expect: http://example.com
|
||||
System.print(a.getAttribute("target")) // expect: _blank
|
||||
System.print(a.getAttribute("nonexistent") == null) // expect: true
|
||||
|
||||
System.print(a.hasAttribute("href")) // expect: true
|
||||
System.print(a.hasAttribute("target")) // expect: true
|
||||
System.print(a.hasAttribute("nonexistent")) // expect: false
|
||||
|
||||
a.setAttribute("title", "Example Link")
|
||||
System.print(a.getAttribute("title")) // expect: Example Link
|
||||
System.print(a.hasAttribute("title")) // expect: true
|
||||
|
||||
a.removeAttribute("target")
|
||||
System.print(a.hasAttribute("target")) // expect: false
|
||||
|
||||
var attrs = a.attributes
|
||||
System.print(attrs["href"]) // expect: http://example.com
|
||||
System.print(attrs["title"]) // expect: Example Link
|
||||
|
||||
28
test/html/element_children.wren
vendored
Normal file
28
test/html/element_children.wren
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div><p>One</p><span>Two</span><a>Three</a></div>")
|
||||
|
||||
var div = doc.querySelector("div")
|
||||
|
||||
System.print(div.children.count) // expect: 3
|
||||
System.print(div.children[0].tagName) // expect: P
|
||||
System.print(div.children[1].tagName) // expect: SPAN
|
||||
System.print(div.children[2].tagName) // expect: A
|
||||
|
||||
System.print(div.childNodes.count >= 3) // expect: true
|
||||
|
||||
System.print(div.firstChild != null) // expect: true
|
||||
System.print(div.lastChild != null) // expect: true
|
||||
|
||||
System.print(div.firstElementChild.tagName) // expect: P
|
||||
System.print(div.lastElementChild.tagName) // expect: A
|
||||
|
||||
var empty = doc.createElement("div")
|
||||
System.print(empty.children.count) // expect: 0
|
||||
System.print(empty.firstChild == null) // expect: true
|
||||
System.print(empty.lastChild == null) // expect: true
|
||||
System.print(empty.firstElementChild == null) // expect: true
|
||||
System.print(empty.lastElementChild == null) // expect: true
|
||||
|
||||
23
test/html/element_classlist.wren
vendored
Normal file
23
test/html/element_classlist.wren
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div class='a b c'>Content</div>")
|
||||
|
||||
var div = doc.querySelector("div")
|
||||
var list = div.classList
|
||||
System.print(list.count) // expect: 3
|
||||
System.print(list[0]) // expect: a
|
||||
System.print(list[1]) // expect: b
|
||||
System.print(list[2]) // expect: c
|
||||
|
||||
var noClass = Document.parse("<span>No Class</span>")
|
||||
var span = noClass.querySelector("span")
|
||||
var emptyList = span.classList
|
||||
System.print(emptyList.count) // expect: 0
|
||||
|
||||
var single = Document.parse("<p class='only'>Text</p>")
|
||||
var p = single.querySelector("p")
|
||||
System.print(p.classList.count) // expect: 1
|
||||
System.print(p.classList[0]) // expect: only
|
||||
|
||||
22
test/html/element_classname.wren
vendored
Normal file
22
test/html/element_classname.wren
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div class='container main'>Content</div>")
|
||||
|
||||
var div = doc.querySelector("div")
|
||||
System.print(div.className) // expect: container main
|
||||
|
||||
div.className = "box"
|
||||
System.print(div.className) // expect: box
|
||||
|
||||
var noClass = Document.parse("<span>No Class</span>")
|
||||
var span = noClass.querySelector("span")
|
||||
System.print(span.className) // expect:
|
||||
|
||||
span.className = "highlight"
|
||||
System.print(span.className) // expect: highlight
|
||||
|
||||
div.className = "one two three"
|
||||
System.print(div.className) // expect: one two three
|
||||
|
||||
25
test/html/element_clonenode.wren
vendored
Normal file
25
test/html/element_clonenode.wren
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id='original' class='box'><p>Child</p><span>Another</span></div>")
|
||||
|
||||
var original = doc.getElementById("original")
|
||||
|
||||
var shallow = original.cloneNode(false)
|
||||
System.print(shallow.tagName) // expect: DIV
|
||||
System.print(shallow.id) // expect: original
|
||||
System.print(shallow.className) // expect: box
|
||||
System.print(shallow.children.count) // expect: 0
|
||||
|
||||
var deep = original.cloneNode(true)
|
||||
System.print(deep.tagName) // expect: DIV
|
||||
System.print(deep.id) // expect: original
|
||||
System.print(deep.children.count) // expect: 2
|
||||
System.print(deep.firstChild.tagName) // expect: P
|
||||
System.print(deep.lastChild.tagName) // expect: SPAN
|
||||
|
||||
deep.id = "cloned"
|
||||
System.print(original.id) // expect: original
|
||||
System.print(deep.id) // expect: cloned
|
||||
|
||||
27
test/html/element_closest.wren
vendored
Normal file
27
test/html/element_closest.wren
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id='outer' class='wrapper'><section id='inner'><p id='text'>Content</p></section></div>")
|
||||
|
||||
var p = doc.getElementById("text")
|
||||
|
||||
var closestSection = p.closest("section")
|
||||
System.print(closestSection != null) // expect: true
|
||||
System.print(closestSection.id) // expect: inner
|
||||
|
||||
var closestDiv = p.closest("div")
|
||||
System.print(closestDiv != null) // expect: true
|
||||
System.print(closestDiv.id) // expect: outer
|
||||
|
||||
var closestWrapper = p.closest(".wrapper")
|
||||
System.print(closestWrapper != null) // expect: true
|
||||
System.print(closestWrapper.id) // expect: outer
|
||||
|
||||
var closestSelf = p.closest("p")
|
||||
System.print(closestSelf != null) // expect: true
|
||||
System.print(closestSelf.id) // expect: text
|
||||
|
||||
var notFound = p.closest("article")
|
||||
System.print(notFound == null) // expect: true
|
||||
|
||||
23
test/html/element_contains.wren
vendored
Normal file
23
test/html/element_contains.wren
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id='outer'><section id='inner'><p id='para'>Text</p></section></div>")
|
||||
|
||||
var outer = doc.getElementById("outer")
|
||||
var inner = doc.getElementById("inner")
|
||||
var para = doc.getElementById("para")
|
||||
|
||||
System.print(outer.contains(inner)) // expect: true
|
||||
System.print(outer.contains(para)) // expect: true
|
||||
System.print(inner.contains(para)) // expect: true
|
||||
|
||||
System.print(inner.contains(outer)) // expect: false
|
||||
System.print(para.contains(outer)) // expect: false
|
||||
System.print(para.contains(inner)) // expect: false
|
||||
|
||||
System.print(outer.contains(outer)) // expect: true
|
||||
|
||||
var unrelated = doc.createElement("span")
|
||||
System.print(outer.contains(unrelated)) // expect: false
|
||||
|
||||
23
test/html/element_dataset.wren
vendored
Normal file
23
test/html/element_dataset.wren
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div data-id='123' data-name='test' data-value='hello'>Content</div>")
|
||||
|
||||
var div = doc.querySelector("div")
|
||||
var dataset = div.dataset
|
||||
|
||||
System.print(dataset["id"]) // expect: 123
|
||||
System.print(dataset["name"]) // expect: test
|
||||
System.print(dataset["value"]) // expect: hello
|
||||
|
||||
var noData = Document.parse("<span>No data</span>")
|
||||
var span = noData.querySelector("span")
|
||||
var emptyDataset = span.dataset
|
||||
System.print(emptyDataset.count) // expect: 0
|
||||
|
||||
var partial = Document.parse("<p data-single='only'>Text</p>")
|
||||
var p = partial.querySelector("p")
|
||||
System.print(p.dataset["single"]) // expect: only
|
||||
System.print(p.dataset.count) // expect: 1
|
||||
|
||||
18
test/html/element_getelementsbyclassname.wren
vendored
Normal file
18
test/html/element_getelementsbyclassname.wren
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id='root'><p class='item'>A</p><span class='item'>B</span><section><a class='item'>C</a></section></div>")
|
||||
|
||||
var root = doc.getElementById("root")
|
||||
var items = root.getElementsByClassName("item")
|
||||
System.print(items.count) // expect: 3
|
||||
|
||||
var section = doc.querySelector("section")
|
||||
var sectionItems = section.getElementsByClassName("item")
|
||||
System.print(sectionItems.count) // expect: 1
|
||||
System.print(sectionItems[0].tagName) // expect: A
|
||||
|
||||
var none = section.getElementsByClassName("nonexistent")
|
||||
System.print(none.count) // expect: 0
|
||||
|
||||
21
test/html/element_getelementsbytagname.wren
vendored
Normal file
21
test/html/element_getelementsbytagname.wren
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id='root'><p>A</p><span>B</span><section><p>C</p></section></div>")
|
||||
|
||||
var root = doc.getElementById("root")
|
||||
var ps = root.getElementsByTagName("p")
|
||||
System.print(ps.count) // expect: 2
|
||||
|
||||
var section = doc.querySelector("section")
|
||||
var sectionPs = section.getElementsByTagName("p")
|
||||
System.print(sectionPs.count) // expect: 1
|
||||
System.print(sectionPs[0].textContent) // expect: C
|
||||
|
||||
var spans = root.getElementsByTagName("span")
|
||||
System.print(spans.count) // expect: 1
|
||||
|
||||
var none = section.getElementsByTagName("span")
|
||||
System.print(none.count) // expect: 0
|
||||
|
||||
21
test/html/element_haschildnodes.wren
vendored
Normal file
21
test/html/element_haschildnodes.wren
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div><p>Text</p></div>")
|
||||
|
||||
var div = doc.querySelector("div")
|
||||
System.print(div.hasChildNodes()) // expect: true
|
||||
|
||||
var p = doc.querySelector("p")
|
||||
System.print(p.hasChildNodes()) // expect: true
|
||||
|
||||
var empty = doc.createElement("span")
|
||||
System.print(empty.hasChildNodes()) // expect: false
|
||||
|
||||
empty.textContent = "Now has content"
|
||||
System.print(empty.hasChildNodes()) // expect: true
|
||||
|
||||
var emptyDiv = Document.parse("<div></div>")
|
||||
System.print(emptyDiv.querySelector("div").hasChildNodes()) // expect: false
|
||||
|
||||
22
test/html/element_id.wren
vendored
Normal file
22
test/html/element_id.wren
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id='myDiv'>Content</div>")
|
||||
|
||||
var div = doc.querySelector("div")
|
||||
System.print(div.id) // expect: myDiv
|
||||
|
||||
div.id = "newId"
|
||||
System.print(div.id) // expect: newId
|
||||
|
||||
var found = doc.getElementById("newId")
|
||||
System.print(found != null) // expect: true
|
||||
|
||||
var noId = Document.parse("<span>No ID</span>")
|
||||
var span = noId.querySelector("span")
|
||||
System.print(span.id) // expect:
|
||||
|
||||
span.id = "spanId"
|
||||
System.print(span.id) // expect: spanId
|
||||
|
||||
23
test/html/element_innerhtml.wren
vendored
Normal file
23
test/html/element_innerhtml.wren
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div><p>Hello</p><span>World</span></div>")
|
||||
|
||||
var div = doc.querySelector("div")
|
||||
var html = div.innerHTML
|
||||
System.print(html.contains("<p>")) // expect: true
|
||||
System.print(html.contains("Hello")) // expect: true
|
||||
System.print(html.contains("<span>")) // expect: true
|
||||
|
||||
div.innerHTML = "<a>Link</a>"
|
||||
System.print(div.innerHTML.contains("<a>")) // expect: true
|
||||
System.print(div.children.count) // expect: 1
|
||||
System.print(div.firstChild.tagName) // expect: A
|
||||
|
||||
var empty = doc.createElement("div")
|
||||
System.print(empty.innerHTML) // expect:
|
||||
|
||||
empty.innerHTML = "Plain text"
|
||||
System.print(empty.textContent) // expect: Plain text
|
||||
|
||||
25
test/html/element_insertbefore.wren
vendored
Normal file
25
test/html/element_insertbefore.wren
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id='container'><span id='ref'>Reference</span></div>")
|
||||
|
||||
var container = doc.getElementById("container")
|
||||
var ref = doc.getElementById("ref")
|
||||
|
||||
var p = doc.createElement("p")
|
||||
p.textContent = "Before"
|
||||
container.insertBefore(p, ref)
|
||||
|
||||
System.print(container.children.count) // expect: 2
|
||||
System.print(container.firstChild.tagName) // expect: P
|
||||
System.print(container.firstChild.textContent) // expect: Before
|
||||
System.print(container.lastChild.tagName) // expect: SPAN
|
||||
|
||||
var a = doc.createElement("a")
|
||||
a.textContent = "Link"
|
||||
container.insertBefore(a, ref)
|
||||
|
||||
System.print(container.children.count) // expect: 3
|
||||
System.print(container.children[1].tagName) // expect: A
|
||||
|
||||
23
test/html/element_matches.wren
vendored
Normal file
23
test/html/element_matches.wren
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id='test' class='box container'><p>Text</p></div>")
|
||||
|
||||
var div = doc.querySelector("div")
|
||||
|
||||
System.print(div.matches("div")) // expect: true
|
||||
System.print(div.matches("#test")) // expect: true
|
||||
System.print(div.matches(".box")) // expect: true
|
||||
System.print(div.matches(".container")) // expect: true
|
||||
System.print(div.matches("div.box")) // expect: true
|
||||
System.print(div.matches("div#test")) // expect: true
|
||||
|
||||
System.print(div.matches("span")) // expect: false
|
||||
System.print(div.matches("#other")) // expect: false
|
||||
System.print(div.matches(".other")) // expect: false
|
||||
|
||||
var p = doc.querySelector("p")
|
||||
System.print(p.matches("p")) // expect: true
|
||||
System.print(p.matches("div")) // expect: false
|
||||
|
||||
24
test/html/element_nodeprops.wren
vendored
Normal file
24
test/html/element_nodeprops.wren
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id='test'>Content</div>")
|
||||
|
||||
var div = doc.querySelector("div")
|
||||
|
||||
System.print(div.nodeType) // expect: 1
|
||||
System.print(div.nodeName) // expect: DIV
|
||||
System.print(div.nodeValue == null) // expect: true
|
||||
|
||||
var text = doc.createTextNode("Text content")
|
||||
System.print(text.nodeType) // expect: 3
|
||||
System.print(text.nodeName) // expect: #text
|
||||
System.print(text.nodeValue) // expect: Text content
|
||||
|
||||
text.nodeValue = "Changed"
|
||||
System.print(text.nodeValue) // expect: Changed
|
||||
|
||||
var p = doc.createElement("p")
|
||||
System.print(p.nodeType) // expect: 1
|
||||
System.print(p.nodeName) // expect: P
|
||||
|
||||
24
test/html/element_normalize.wren
vendored
Normal file
24
test/html/element_normalize.wren
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id='container'></div>")
|
||||
var container = doc.getElementById("container")
|
||||
|
||||
container.appendChild(doc.createTextNode("Hello "))
|
||||
container.appendChild(doc.createTextNode("World"))
|
||||
container.appendChild(doc.createTextNode("!"))
|
||||
|
||||
System.print(container.childNodes.count) // expect: 3
|
||||
|
||||
container.normalize()
|
||||
|
||||
System.print(container.childNodes.count) // expect: 1
|
||||
System.print(container.textContent) // expect: Hello World!
|
||||
|
||||
var simple = Document.parse("<p>Simple text</p>")
|
||||
var p = simple.querySelector("p")
|
||||
var before = p.childNodes.count
|
||||
p.normalize()
|
||||
System.print(p.childNodes.count == before) // expect: true
|
||||
|
||||
22
test/html/element_outerhtml.wren
vendored
Normal file
22
test/html/element_outerhtml.wren
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id='test'><p>Hello</p></div>")
|
||||
|
||||
var div = doc.querySelector("div")
|
||||
var html = div.outerHTML
|
||||
System.print(html.contains("<div")) // expect: true
|
||||
System.print(html.contains("id=\"test\"") || html.contains("id='test'")) // expect: true
|
||||
System.print(html.contains("</div>")) // expect: true
|
||||
System.print(html.contains("<p>")) // expect: true
|
||||
|
||||
var p = doc.querySelector("p")
|
||||
System.print(p.outerHTML.contains("<p>")) // expect: true
|
||||
System.print(p.outerHTML.contains("Hello")) // expect: true
|
||||
System.print(p.outerHTML.contains("</p>")) // expect: true
|
||||
|
||||
var selfClose = Document.parse("<br/>")
|
||||
var br = selfClose.querySelector("br")
|
||||
System.print(br != null) // expect: true
|
||||
|
||||
24
test/html/element_parent.wren
vendored
Normal file
24
test/html/element_parent.wren
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id='parent'><p id='child'>Text</p></div>")
|
||||
|
||||
var child = doc.getElementById("child")
|
||||
var parent = child.parentNode
|
||||
System.print(parent != null) // expect: true
|
||||
System.print(parent.id) // expect: parent
|
||||
|
||||
var parentElem = child.parentElement
|
||||
System.print(parentElem != null) // expect: true
|
||||
System.print(parentElem.tagName) // expect: DIV
|
||||
|
||||
var div = doc.getElementById("parent")
|
||||
var divParent = div.parentNode
|
||||
System.print(divParent != null) // expect: true
|
||||
|
||||
var nested = Document.parse("<div><span><a>Link</a></span></div>")
|
||||
var a = nested.querySelector("a")
|
||||
System.print(a.parentNode.tagName) // expect: SPAN
|
||||
System.print(a.parentElement.tagName) // expect: SPAN
|
||||
|
||||
25
test/html/element_queryselector.wren
vendored
Normal file
25
test/html/element_queryselector.wren
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id='root'><section><p class='text'>One</p></section><article><p class='text'>Two</p></article></div>")
|
||||
|
||||
var root = doc.getElementById("root")
|
||||
var section = doc.querySelector("section")
|
||||
var article = doc.querySelector("article")
|
||||
|
||||
var fromRoot = root.querySelector("p")
|
||||
System.print(fromRoot.textContent) // expect: One
|
||||
|
||||
var fromSection = section.querySelector(".text")
|
||||
System.print(fromSection.textContent) // expect: One
|
||||
|
||||
var fromArticle = article.querySelector(".text")
|
||||
System.print(fromArticle.textContent) // expect: Two
|
||||
|
||||
var notFound = section.querySelector("article")
|
||||
System.print(notFound == null) // expect: true
|
||||
|
||||
var nested = root.querySelector("section p")
|
||||
System.print(nested.textContent) // expect: One
|
||||
|
||||
24
test/html/element_queryselectorall.wren
vendored
Normal file
24
test/html/element_queryselectorall.wren
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id='root'><section><p>A</p><p>B</p></section><article><p>C</p></article></div>")
|
||||
|
||||
var root = doc.getElementById("root")
|
||||
var all = root.querySelectorAll("p")
|
||||
System.print(all.count) // expect: 3
|
||||
|
||||
var section = doc.querySelector("section")
|
||||
var sectionPs = section.querySelectorAll("p")
|
||||
System.print(sectionPs.count) // expect: 2
|
||||
System.print(sectionPs[0].textContent) // expect: A
|
||||
System.print(sectionPs[1].textContent) // expect: B
|
||||
|
||||
var article = doc.querySelector("article")
|
||||
var articlePs = article.querySelectorAll("p")
|
||||
System.print(articlePs.count) // expect: 1
|
||||
System.print(articlePs[0].textContent) // expect: C
|
||||
|
||||
var none = section.querySelectorAll("article")
|
||||
System.print(none.count) // expect: 0
|
||||
|
||||
23
test/html/element_remove.wren
vendored
Normal file
23
test/html/element_remove.wren
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id='container'><p id='para'>Text</p><span>Other</span></div>")
|
||||
|
||||
var container = doc.getElementById("container")
|
||||
System.print(container.children.count) // expect: 2
|
||||
|
||||
var para = doc.getElementById("para")
|
||||
para.remove()
|
||||
|
||||
System.print(container.children.count) // expect: 1
|
||||
System.print(container.firstChild.tagName) // expect: SPAN
|
||||
|
||||
var doc2 = Document.parse("<ul><li>A</li><li>B</li><li>C</li></ul>")
|
||||
var ul = doc2.querySelector("ul")
|
||||
var items = doc2.querySelectorAll("li")
|
||||
System.print(items.count) // expect: 3
|
||||
|
||||
items[1].remove()
|
||||
System.print(ul.children.count) // expect: 2
|
||||
|
||||
21
test/html/element_removechild.wren
vendored
Normal file
21
test/html/element_removechild.wren
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id='container'><p>One</p><span>Two</span><a>Three</a></div>")
|
||||
|
||||
var container = doc.getElementById("container")
|
||||
System.print(container.children.count) // expect: 3
|
||||
|
||||
var span = doc.querySelector("span")
|
||||
container.removeChild(span)
|
||||
System.print(container.children.count) // expect: 2
|
||||
|
||||
System.print(container.firstChild.tagName) // expect: P
|
||||
System.print(container.lastChild.tagName) // expect: A
|
||||
|
||||
var p = doc.querySelector("p")
|
||||
container.removeChild(p)
|
||||
System.print(container.children.count) // expect: 1
|
||||
System.print(container.firstChild.tagName) // expect: A
|
||||
|
||||
26
test/html/element_replacechild.wren
vendored
Normal file
26
test/html/element_replacechild.wren
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id='container'><p id='old'>Old</p></div>")
|
||||
|
||||
var container = doc.getElementById("container")
|
||||
var old = doc.getElementById("old")
|
||||
|
||||
var newElem = doc.createElement("span")
|
||||
newElem.textContent = "New"
|
||||
|
||||
container.replaceChild(newElem, old)
|
||||
|
||||
System.print(container.children.count) // expect: 1
|
||||
System.print(container.firstChild.tagName) // expect: SPAN
|
||||
System.print(container.firstChild.textContent) // expect: New
|
||||
|
||||
var doc2 = Document.parse("<div><a>First</a><b>Second</b></div>")
|
||||
var div = doc2.querySelector("div")
|
||||
var a = doc2.querySelector("a")
|
||||
var replacement = doc2.createElement("em")
|
||||
replacement.textContent = "Replaced"
|
||||
div.replaceChild(replacement, a)
|
||||
System.print(div.firstChild.tagName) // expect: EM
|
||||
|
||||
22
test/html/element_siblings.wren
vendored
Normal file
22
test/html/element_siblings.wren
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div><p>One</p><span>Two</span><a>Three</a></div>")
|
||||
|
||||
var span = doc.querySelector("span")
|
||||
|
||||
System.print(span.previousSibling != null) // expect: true
|
||||
System.print(span.nextSibling != null) // expect: true
|
||||
|
||||
System.print(span.previousElementSibling.tagName) // expect: P
|
||||
System.print(span.nextElementSibling.tagName) // expect: A
|
||||
|
||||
var p = doc.querySelector("p")
|
||||
System.print(p.previousElementSibling == null) // expect: true
|
||||
System.print(p.nextElementSibling.tagName) // expect: SPAN
|
||||
|
||||
var a = doc.querySelector("a")
|
||||
System.print(a.previousElementSibling.tagName) // expect: SPAN
|
||||
System.print(a.nextElementSibling == null) // expect: true
|
||||
|
||||
18
test/html/element_tagname.wren
vendored
Normal file
18
test/html/element_tagname.wren
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div><p>Para</p><span>Span</span><a>Link</a></div>")
|
||||
|
||||
var div = doc.querySelector("div")
|
||||
System.print(div.tagName) // expect: DIV
|
||||
|
||||
var p = doc.querySelector("p")
|
||||
System.print(p.tagName) // expect: P
|
||||
|
||||
var span = doc.querySelector("span")
|
||||
System.print(span.tagName) // expect: SPAN
|
||||
|
||||
var a = doc.querySelector("a")
|
||||
System.print(a.tagName) // expect: A
|
||||
|
||||
25
test/html/element_textcontent.wren
vendored
Normal file
25
test/html/element_textcontent.wren
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div>Hello <span>World</span></div>")
|
||||
|
||||
var div = doc.querySelector("div")
|
||||
System.print(div.textContent.contains("Hello")) // expect: true
|
||||
System.print(div.textContent.contains("World")) // expect: true
|
||||
|
||||
var span = doc.querySelector("span")
|
||||
System.print(span.textContent) // expect: World
|
||||
|
||||
span.textContent = "Universe"
|
||||
System.print(span.textContent) // expect: Universe
|
||||
|
||||
var empty = doc.createElement("p")
|
||||
System.print(empty.textContent) // expect:
|
||||
|
||||
empty.textContent = "New content"
|
||||
System.print(empty.textContent) // expect: New content
|
||||
|
||||
var nested = Document.parse("<div><p><span>Deep</span></p></div>")
|
||||
System.print(nested.querySelector("div").textContent.contains("Deep")) // expect: true
|
||||
|
||||
19
test/html/nodelist_count.wren
vendored
Normal file
19
test/html/nodelist_count.wren
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div><p>A</p><p>B</p><p>C</p></div>")
|
||||
|
||||
var list = doc.querySelectorAll("p")
|
||||
System.print(list.count) // expect: 3
|
||||
|
||||
var empty = doc.querySelectorAll(".nonexistent")
|
||||
System.print(empty.count) // expect: 0
|
||||
|
||||
var single = doc.querySelectorAll("div")
|
||||
System.print(single.count) // expect: 1
|
||||
|
||||
var many = Document.parse("<ul><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li></ul>")
|
||||
var items = many.querySelectorAll("li")
|
||||
System.print(items.count) // expect: 5
|
||||
|
||||
20
test/html/nodelist_foreach.wren
vendored
Normal file
20
test/html/nodelist_foreach.wren
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div><p>A</p><p>B</p><p>C</p></div>")
|
||||
|
||||
var list = doc.querySelectorAll("p")
|
||||
var count = 0
|
||||
var texts = []
|
||||
|
||||
list.forEach {|elem|
|
||||
count = count + 1
|
||||
texts.add(elem.textContent)
|
||||
}
|
||||
|
||||
System.print(count) // expect: 3
|
||||
System.print(texts[0]) // expect: A
|
||||
System.print(texts[1]) // expect: B
|
||||
System.print(texts[2]) // expect: C
|
||||
|
||||
19
test/html/nodelist_item.wren
vendored
Normal file
19
test/html/nodelist_item.wren
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div><p>A</p><p>B</p><p>C</p></div>")
|
||||
|
||||
var list = doc.querySelectorAll("p")
|
||||
|
||||
System.print(list.item(0).textContent) // expect: A
|
||||
System.print(list.item(1).textContent) // expect: B
|
||||
System.print(list.item(2).textContent) // expect: C
|
||||
|
||||
System.print(list.item(0).tagName) // expect: P
|
||||
System.print(list.item(1).tagName) // expect: P
|
||||
System.print(list.item(2).tagName) // expect: P
|
||||
|
||||
var single = doc.querySelectorAll("div")
|
||||
System.print(single.item(0).tagName) // expect: DIV
|
||||
|
||||
22
test/html/nodelist_subscript.wren
vendored
Normal file
22
test/html/nodelist_subscript.wren
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div><p>A</p><p>B</p><p>C</p></div>")
|
||||
|
||||
var list = doc.querySelectorAll("p")
|
||||
|
||||
System.print(list[0].textContent) // expect: A
|
||||
System.print(list[1].textContent) // expect: B
|
||||
System.print(list[2].textContent) // expect: C
|
||||
|
||||
System.print(list[0].tagName) // expect: P
|
||||
System.print(list[1].tagName) // expect: P
|
||||
System.print(list[2].tagName) // expect: P
|
||||
|
||||
var mixed = Document.parse("<div><span>X</span><a>Y</a><em>Z</em></div>")
|
||||
var children = mixed.querySelector("div").children
|
||||
System.print(children[0].tagName) // expect: SPAN
|
||||
System.print(children[1].tagName) // expect: A
|
||||
System.print(children[2].tagName) // expect: EM
|
||||
|
||||
20
test/html/nodelist_tolist.wren
vendored
Normal file
20
test/html/nodelist_tolist.wren
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div><p>A</p><p>B</p><p>C</p></div>")
|
||||
|
||||
var nodeList = doc.querySelectorAll("p")
|
||||
var list = nodeList.toList
|
||||
|
||||
System.print(list.count) // expect: 3
|
||||
System.print(list[0].textContent) // expect: A
|
||||
System.print(list[1].textContent) // expect: B
|
||||
System.print(list[2].textContent) // expect: C
|
||||
|
||||
System.print(list is List) // expect: true
|
||||
|
||||
var empty = doc.querySelectorAll(".none").toList
|
||||
System.print(empty.count) // expect: 0
|
||||
System.print(empty is List) // expect: true
|
||||
|
||||
19
test/html/selectors_adjacent.wren
vendored
Normal file
19
test/html/selectors_adjacent.wren
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div><p>Para</p><span>Span</span><a>Anchor</a></div>")
|
||||
|
||||
System.print(doc.querySelector("p + span") != null) // expect: true
|
||||
System.print(doc.querySelector("p + span").textContent) // expect: Span
|
||||
|
||||
System.print(doc.querySelector("span + a") != null) // expect: true
|
||||
System.print(doc.querySelector("span + a").textContent) // expect: Anchor
|
||||
|
||||
System.print(doc.querySelector("p + a") == null) // expect: true
|
||||
|
||||
System.print(doc.querySelector("a + p") == null) // expect: true
|
||||
|
||||
var multi = Document.parse("<div><p>A</p><p>B</p><p>C</p></div>")
|
||||
System.print(multi.querySelectorAll("p + p").count) // expect: 2
|
||||
|
||||
21
test/html/selectors_attribute.wren
vendored
Normal file
21
test/html/selectors_attribute.wren
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<a href='http://example.com' target='_blank' data-id='123'>Link</a>")
|
||||
|
||||
System.print(doc.querySelector("[href]") != null) // expect: true
|
||||
System.print(doc.querySelector("[target]") != null) // expect: true
|
||||
System.print(doc.querySelector("[data-id]") != null) // expect: true
|
||||
|
||||
System.print(doc.querySelector("[nonexistent]") == null) // expect: true
|
||||
|
||||
System.print(doc.querySelector("[href='http://example.com']") != null) // expect: true
|
||||
System.print(doc.querySelector("[target='_blank']") != null) // expect: true
|
||||
System.print(doc.querySelector("[data-id='123']") != null) // expect: true
|
||||
|
||||
System.print(doc.querySelector("[href='wrong']") == null) // expect: true
|
||||
|
||||
System.print(doc.querySelector("a[href]") != null) // expect: true
|
||||
System.print(doc.querySelector("a[target='_blank']") != null) // expect: true
|
||||
|
||||
17
test/html/selectors_child.wren
vendored
Normal file
17
test/html/selectors_child.wren
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div><p>Direct</p><span><p>Nested</p></span></div>")
|
||||
|
||||
System.print(doc.querySelector("div > p") != null) // expect: true
|
||||
System.print(doc.querySelector("div > p").textContent) // expect: Direct
|
||||
|
||||
System.print(doc.querySelector("span > p") != null) // expect: true
|
||||
System.print(doc.querySelector("span > p").textContent) // expect: Nested
|
||||
|
||||
System.print(doc.querySelectorAll("div > p").count) // expect: 1
|
||||
|
||||
var nested = Document.parse("<ul><li>A</li><li><ul><li>B</li></ul></li></ul>")
|
||||
System.print(nested.querySelectorAll("ul > li").count) // expect: 3
|
||||
|
||||
20
test/html/selectors_class.wren
vendored
Normal file
20
test/html/selectors_class.wren
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div class='box'><p class='text highlight'>Text</p></div>")
|
||||
|
||||
System.print(doc.querySelector(".box") != null) // expect: true
|
||||
System.print(doc.querySelector(".box").tagName) // expect: DIV
|
||||
|
||||
System.print(doc.querySelector(".text") != null) // expect: true
|
||||
System.print(doc.querySelector(".highlight") != null) // expect: true
|
||||
|
||||
System.print(doc.querySelector(".nonexistent") == null) // expect: true
|
||||
|
||||
System.print(doc.querySelector("div.box") != null) // expect: true
|
||||
System.print(doc.querySelector("p.text") != null) // expect: true
|
||||
System.print(doc.querySelector("p.highlight") != null) // expect: true
|
||||
|
||||
System.print(doc.querySelector(".text.highlight") != null) // expect: true
|
||||
|
||||
19
test/html/selectors_combined.wren
vendored
Normal file
19
test/html/selectors_combined.wren
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id='main' class='container'><p class='text'>Content</p></div>")
|
||||
|
||||
System.print(doc.querySelector("div#main.container") != null) // expect: true
|
||||
System.print(doc.querySelector("#main.container") != null) // expect: true
|
||||
System.print(doc.querySelector("div.container#main") != null) // expect: true
|
||||
|
||||
System.print(doc.querySelector("div#main.container p.text") != null) // expect: true
|
||||
System.print(doc.querySelector("#main > p.text") != null) // expect: true
|
||||
|
||||
System.print(doc.querySelector("div#main.wrong") == null) // expect: true
|
||||
System.print(doc.querySelector("span#main.container") == null) // expect: true
|
||||
|
||||
var complex = Document.parse("<form id='login'><input type='text' class='field' name='user'/></form>")
|
||||
System.print(complex.querySelector("form#login input.field[type='text']") != null) // expect: true
|
||||
|
||||
17
test/html/selectors_descendant.wren
vendored
Normal file
17
test/html/selectors_descendant.wren
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div><section><p>Deep</p></section></div>")
|
||||
|
||||
System.print(doc.querySelector("div p") != null) // expect: true
|
||||
System.print(doc.querySelector("div section") != null) // expect: true
|
||||
System.print(doc.querySelector("section p") != null) // expect: true
|
||||
System.print(doc.querySelector("div section p") != null) // expect: true
|
||||
|
||||
System.print(doc.querySelector("div span") == null) // expect: true
|
||||
|
||||
var multi = Document.parse("<div><p>A</p><span><p>B</p></span></div>")
|
||||
System.print(multi.querySelectorAll("div p").count) // expect: 2
|
||||
System.print(multi.querySelectorAll("span p").count) // expect: 1
|
||||
|
||||
18
test/html/selectors_general_sibling.wren
vendored
Normal file
18
test/html/selectors_general_sibling.wren
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div><p>Para</p><span>Span</span><a>Anchor</a><em>Em</em></div>")
|
||||
|
||||
System.print(doc.querySelector("p ~ span") != null) // expect: true
|
||||
System.print(doc.querySelector("p ~ a") != null) // expect: true
|
||||
System.print(doc.querySelector("p ~ em") != null) // expect: true
|
||||
|
||||
System.print(doc.querySelector("span ~ a") != null) // expect: true
|
||||
System.print(doc.querySelector("span ~ em") != null) // expect: true
|
||||
|
||||
System.print(doc.querySelector("a ~ p") == null) // expect: true
|
||||
System.print(doc.querySelector("em ~ p") == null) // expect: true
|
||||
|
||||
System.print(doc.querySelectorAll("p ~ *").count) // expect: 3
|
||||
|
||||
18
test/html/selectors_id.wren
vendored
Normal file
18
test/html/selectors_id.wren
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id='first'><p id='second'>Text</p></div>")
|
||||
|
||||
System.print(doc.querySelector("#first") != null) // expect: true
|
||||
System.print(doc.querySelector("#first").tagName) // expect: DIV
|
||||
|
||||
System.print(doc.querySelector("#second") != null) // expect: true
|
||||
System.print(doc.querySelector("#second").tagName) // expect: P
|
||||
|
||||
System.print(doc.querySelector("#nonexistent") == null) // expect: true
|
||||
|
||||
System.print(doc.querySelector("div#first") != null) // expect: true
|
||||
System.print(doc.querySelector("p#second") != null) // expect: true
|
||||
System.print(doc.querySelector("span#first") == null) // expect: true
|
||||
|
||||
15
test/html/selectors_pseudo_firstchild.wren
vendored
Normal file
15
test/html/selectors_pseudo_firstchild.wren
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<ul><li>A</li><li>B</li><li>C</li></ul>")
|
||||
|
||||
System.print(doc.querySelector("li:first-child") != null) // expect: true
|
||||
System.print(doc.querySelector("li:first-child").textContent) // expect: A
|
||||
|
||||
var multi = Document.parse("<div><p>First</p><span>Second</span></div><div><a>Third</a></div>")
|
||||
System.print(multi.querySelectorAll(":first-child").count >= 2) // expect: true
|
||||
|
||||
var nested = Document.parse("<div><span><em>Nested First</em></span></div>")
|
||||
System.print(nested.querySelector("em:first-child") != null) // expect: true
|
||||
|
||||
15
test/html/selectors_pseudo_lastchild.wren
vendored
Normal file
15
test/html/selectors_pseudo_lastchild.wren
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<ul><li>A</li><li>B</li><li>C</li></ul>")
|
||||
|
||||
System.print(doc.querySelector("li:last-child") != null) // expect: true
|
||||
System.print(doc.querySelector("li:last-child").textContent) // expect: C
|
||||
|
||||
var multi = Document.parse("<div><p>First</p><span>Last</span></div>")
|
||||
System.print(multi.querySelector("span:last-child") != null) // expect: true
|
||||
System.print(multi.querySelector("span:last-child").textContent) // expect: Last
|
||||
|
||||
System.print(multi.querySelector("p:last-child") == null) // expect: true
|
||||
|
||||
17
test/html/selectors_pseudo_nthchild.wren
vendored
Normal file
17
test/html/selectors_pseudo_nthchild.wren
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<ul><li>A</li><li>B</li><li>C</li><li>D</li><li>E</li></ul>")
|
||||
|
||||
System.print(doc.querySelector("li:nth-child(1)") != null) // expect: true
|
||||
System.print(doc.querySelector("li:nth-child(1)").textContent) // expect: A
|
||||
|
||||
System.print(doc.querySelector("li:nth-child(3)") != null) // expect: true
|
||||
System.print(doc.querySelector("li:nth-child(3)").textContent) // expect: C
|
||||
|
||||
System.print(doc.querySelector("li:nth-child(5)") != null) // expect: true
|
||||
System.print(doc.querySelector("li:nth-child(5)").textContent) // expect: E
|
||||
|
||||
System.print(doc.querySelector("li:nth-child(6)") == null) // expect: true
|
||||
|
||||
20
test/html/selectors_tag.wren
vendored
Normal file
20
test/html/selectors_tag.wren
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div><p>Para</p><span>Span</span><a>Anchor</a></div>")
|
||||
|
||||
System.print(doc.querySelector("div") != null) // expect: true
|
||||
System.print(doc.querySelector("p") != null) // expect: true
|
||||
System.print(doc.querySelector("span") != null) // expect: true
|
||||
System.print(doc.querySelector("a") != null) // expect: true
|
||||
|
||||
System.print(doc.querySelector("article") == null) // expect: true
|
||||
System.print(doc.querySelector("section") == null) // expect: true
|
||||
|
||||
System.print(doc.querySelectorAll("div").count) // expect: 1
|
||||
System.print(doc.querySelectorAll("p").count) // expect: 1
|
||||
|
||||
var nested = Document.parse("<div><div><div></div></div></div>")
|
||||
System.print(nested.querySelectorAll("div").count) // expect: 3
|
||||
|
||||
21
test/html/serialization_basic.wren
vendored
Normal file
21
test/html/serialization_basic.wren
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id='test' class='box'>Content</div>")
|
||||
|
||||
var html = doc.outerHTML
|
||||
System.print(html.contains("<div")) // expect: true
|
||||
System.print(html.contains("</div>")) // expect: true
|
||||
System.print(html.contains("Content")) // expect: true
|
||||
|
||||
var div = doc.querySelector("div")
|
||||
var outer = div.outerHTML
|
||||
System.print(outer.contains("<div")) // expect: true
|
||||
System.print(outer.contains("id=")) // expect: true
|
||||
System.print(outer.contains("class=")) // expect: true
|
||||
|
||||
var inner = div.innerHTML
|
||||
System.print(inner.contains("Content")) // expect: true
|
||||
System.print(inner.contains("<div") == false) // expect: true
|
||||
|
||||
21
test/html/serialization_entities.wren
vendored
Normal file
21
test/html/serialization_entities.wren
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<p><tag></p>")
|
||||
System.print(doc.querySelector("p").textContent) // expect: <tag>
|
||||
|
||||
var doc2 = Document.parse("<p>&</p>")
|
||||
System.print(doc2.querySelector("p").textContent) // expect: &
|
||||
|
||||
var doc3 = Document.parse("<p>"quoted"</p>")
|
||||
System.print(doc3.querySelector("p").textContent) // expect: "quoted"
|
||||
|
||||
var doc4 = Document.parse("<p> </p>")
|
||||
var text4 = doc4.querySelector("p").textContent
|
||||
System.print(text4.count > 0) // expect: true
|
||||
|
||||
var doc5 = Document.parse("<p>© 2024</p>")
|
||||
var text5 = doc5.querySelector("p").textContent
|
||||
System.print(text5.contains("2024")) // expect: true
|
||||
|
||||
19
test/html/serialization_selfclosing.wren
vendored
Normal file
19
test/html/serialization_selfclosing.wren
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div><br/><hr/><img src='test.jpg'/><input type='text'/></div>")
|
||||
|
||||
var div = doc.querySelector("div")
|
||||
var html = div.innerHTML
|
||||
|
||||
System.print(html.contains("br")) // expect: true
|
||||
System.print(html.contains("hr")) // expect: true
|
||||
System.print(html.contains("img")) // expect: true
|
||||
System.print(html.contains("input")) // expect: true
|
||||
|
||||
System.print(doc.querySelector("br") != null) // expect: true
|
||||
System.print(doc.querySelector("hr") != null) // expect: true
|
||||
System.print(doc.querySelector("img") != null) // expect: true
|
||||
System.print(doc.querySelector("input") != null) // expect: true
|
||||
|
||||
20
test/html/textnode_clonenode.wren
vendored
Normal file
20
test/html/textnode_clonenode.wren
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div></div>")
|
||||
|
||||
var original = doc.createTextNode("Original text")
|
||||
System.print(original.textContent) // expect: Original text
|
||||
|
||||
var clone = original.cloneNode(true)
|
||||
System.print(clone.textContent) // expect: Original text
|
||||
System.print(clone.nodeType) // expect: 3
|
||||
|
||||
clone.textContent = "Cloned text"
|
||||
System.print(original.textContent) // expect: Original text
|
||||
System.print(clone.textContent) // expect: Cloned text
|
||||
|
||||
var shallowClone = original.cloneNode(false)
|
||||
System.print(shallowClone.textContent) // expect: Original text
|
||||
|
||||
25
test/html/textnode_properties.wren
vendored
Normal file
25
test/html/textnode_properties.wren
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id='container'></div>")
|
||||
var container = doc.getElementById("container")
|
||||
|
||||
var text = doc.createTextNode("Hello World")
|
||||
|
||||
System.print(text.textContent) // expect: Hello World
|
||||
System.print(text.nodeType) // expect: 3
|
||||
System.print(text.nodeName) // expect: #text
|
||||
System.print(text.nodeValue) // expect: Hello World
|
||||
|
||||
text.textContent = "Changed content"
|
||||
System.print(text.textContent) // expect: Changed content
|
||||
|
||||
text.nodeValue = "Another change"
|
||||
System.print(text.nodeValue) // expect: Another change
|
||||
System.print(text.textContent) // expect: Another change
|
||||
|
||||
container.appendChild(text)
|
||||
System.print(text.parentNode != null) // expect: true
|
||||
System.print(text.parentElement.tagName) // expect: DIV
|
||||
|
||||
20
test/html/textnode_remove.wren
vendored
Normal file
20
test/html/textnode_remove.wren
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id='container'>Text content</div>")
|
||||
var container = doc.getElementById("container")
|
||||
|
||||
System.print(container.textContent.contains("Text content")) // expect: true
|
||||
|
||||
var textNodes = container.childNodes
|
||||
System.print(textNodes.count >= 1) // expect: true
|
||||
|
||||
var text = doc.createTextNode("Extra text")
|
||||
container.appendChild(text)
|
||||
|
||||
var before = container.childNodes.count
|
||||
text.remove()
|
||||
var after = container.childNodes.count
|
||||
System.print(after < before) // expect: true
|
||||
|
||||
24
test/html/textnode_siblings.wren
vendored
Normal file
24
test/html/textnode_siblings.wren
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "html" for Document
|
||||
|
||||
var doc = Document.parse("<div id='container'></div>")
|
||||
var container = doc.getElementById("container")
|
||||
|
||||
var text1 = doc.createTextNode("First")
|
||||
var text2 = doc.createTextNode("Second")
|
||||
var text3 = doc.createTextNode("Third")
|
||||
|
||||
container.appendChild(text1)
|
||||
container.appendChild(text2)
|
||||
container.appendChild(text3)
|
||||
|
||||
System.print(text1.previousSibling == null) // expect: true
|
||||
System.print(text1.nextSibling != null) // expect: true
|
||||
|
||||
System.print(text2.previousSibling != null) // expect: true
|
||||
System.print(text2.nextSibling != null) // expect: true
|
||||
|
||||
System.print(text3.previousSibling != null) // expect: true
|
||||
System.print(text3.nextSibling == null) // expect: true
|
||||
|
||||
Loading…
Reference in New Issue
Block a user