-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 19fe4f5
Showing
11 changed files
with
1,201 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
node_modules/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
<!DOCTYPE html> | ||
<meta charset="UTF-8"> | ||
<title>Missing Piece Tests</title> | ||
<script src="./missing-piece.js"></script> | ||
|
||
<h1>Missing Piece Tests</h1> | ||
|
||
<h2>Parsing</h2> | ||
|
||
<button action="/test/responses/text.html" method=GET target="_this">Plain Text</button> | ||
<button action="/test/responses/h3.html" method=GET target="_this">H3</button> | ||
<button action="/test/responses/red-h3.html" method=GET target="_this">Red H3</button> | ||
<button action="/test/responses/all-blue.html" method=GET target="_this">color: blue</button> | ||
|
||
<h2>Forms</h2> | ||
|
||
<form action="/test/responses/text" method=GET> | ||
<input type=hidden name=key value=tst> | ||
<button>Get</button> | ||
</form> | ||
|
||
<form action="/test/responses/text" method=GET target="_this"> | ||
<input type=hidden name=key value=tst> | ||
<button>Get (self target)</button> | ||
</form> | ||
|
||
<form action="/test/responses/text" method=PUT> | ||
<input type=hidden name=key value=tst> | ||
<button>Put</button> | ||
</form> | ||
|
||
<form action="/test/responses/text" method=DELETE> | ||
<input type=hidden name=key value=tst> | ||
<button>Delete</button> | ||
</form> | ||
|
||
|
||
<h2>Buttons</h2> | ||
<button action="/test/responses/text.html">Get (default)</button> | ||
<button action="/test/responses/text.html" method=GET>Get</button> | ||
<button action="/test/responses/text.html" method=POST>Post</button> | ||
<button action="/test/responses/text.html" method=PUT>Put</button> | ||
<button action="/test/responses/text.html" method=DELETE>Delete</button> | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"compilerOptions": { | ||
"target": "es2020", | ||
"checkJs": true | ||
}, | ||
"include": ["*"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
const ADDITIONAL_FORM_METHODS = ['PUT', 'PATCH', 'DELETE'] | ||
const EXISTING_TARGET_KEYWORDS = ['_self', '_blank', '_parent', '_top', '_unfencedTop'] | ||
|
||
/** | ||
* @param {string} url | ||
* @param {string} method | ||
* @param {FormData} data | ||
* @param {string} target | ||
*/ | ||
function ajax(url, method, data, target) { | ||
/** @param {Event} e */ | ||
return async (e) => { | ||
e.preventDefault() // The new actions override old ones | ||
|
||
|
||
let targetElement | ||
if (target === '_this') { | ||
targetElement = e.target | ||
} else if (target) { | ||
targetElement = document.querySelector('target') | ||
if (!targetElement) { | ||
console.error(`no element found for target ${target} - ignorning`) | ||
return null | ||
} | ||
} | ||
|
||
const opts = { method } | ||
if (method !== 'GET' && method !== 'DELETE') { | ||
opts.body = data | ||
} // else convert to URL params | ||
|
||
const res = await fetch(url, opts) | ||
const responseText = await res.text() | ||
if (targetElement) { | ||
const template = document.createElement('template') | ||
template.innerHTML = responseText | ||
processNode(template) | ||
console.log(template.content.children) | ||
|
||
// @ts-ignore - all the targets are going to be Elements | ||
targetElement.replaceWith(template.content) | ||
} else { | ||
// TODO check for html wrapper? | ||
document.querySelector('html').innerHTML = responseText | ||
processNode(document) | ||
// TODO push to url and history | ||
// Handle redirects | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* @param {Document | Element} node | ||
*/ | ||
function processNode(node) { | ||
// #1 forms can PUT, PATCH, and DELETE | ||
const forms = node.querySelectorAll('form') | ||
for (const form of forms) { | ||
const method = form.getAttribute('method') | ||
const target = form.getAttribute('target') || undefined | ||
// Only process forms that a) have subtree targets or b) have new methods | ||
// TODO move this into query selector? | ||
if (target || ADDITIONAL_FORM_METHODS.includes(method)) { | ||
const url = form.getAttribute('action') | ||
const data = new FormData(form) | ||
form.addEventListener('submit', ajax(url, method, data, target)) | ||
} | ||
} | ||
|
||
// #2 buttons can make requests on their own | ||
const buttons = node.querySelectorAll('button[action]') | ||
for (const button of buttons) { | ||
const url = button.getAttribute('action') | ||
const target = button.getAttribute('target') | ||
const method = button.getAttribute('method') || 'GET' | ||
// TODO add the name/value if appropriate | ||
button.addEventListener('click', ajax(url, method, undefined, target)) | ||
} | ||
|
||
// #3 Links, buttons and forms can target | ||
const links = node.querySelectorAll('a[target]') | ||
for (const link of links) { | ||
const target = link.getAttribute('target') | ||
const hasExistingBehavior = EXISTING_TARGET_KEYWORDS.includes(target) || | ||
document.querySelector(`iframe[name=${target}]`) | ||
|
||
const url = link.getAttribute('href') | ||
if (!hasExistingBehavior) { | ||
link.addEventListener('click', ajax(url, 'GET', undefined, target)) | ||
} | ||
} | ||
} | ||
|
||
if (document.readyState === "loading") { | ||
document.addEventListener("DOMContentLoaded", () => { processNode(document) }); | ||
} else { | ||
processNode(document) | ||
} |
Oops, something went wrong.