le big re-rename

This commit is contained in:
carson 2020-05-17 05:22:19 -07:00
parent e1e4f25b0e
commit 3aa8c64754
90 changed files with 1061 additions and 2329 deletions

View File

@ -1,28 +1,28 @@
![</> kutty](https://github.com/bigskysoftware/kutty/raw/master/www/img/kutty_logo.1.png "high power tools for HTML")
![</> htmx](https://github.com/bigskysoftware/htmx/raw/master/www/img/htmx_logo.1.png "high power tools for HTML")
*high power tools for HTML*
## Introduction
Kutty is a set of extensions (attributes, request headers, etc.) that help you build
[modern UI](https://kutty.org/examples) with the [simplicity](https://en.wikipedia.org/wiki/HATEOAS) and
[modern UI](https://htmx.org/examples) with the [simplicity](https://en.wikipedia.org/wiki/HATEOAS) and
[power](https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm) of HTML.
Kutty is small ([~6k min.gz'd](https://unpkg.com/kutty.org/dist/)), IE11 compatible, [dependency-free](https://github.com/bigskysoftware/kutty/blob/master/package.json)
Kutty is small ([~6k min.gz'd](https://unpkg.com/htmx.org/dist/)), IE11 compatible, [dependency-free](https://github.com/bigskysoftware/htmx/blob/master/package.json)
& you can try it out quickly, without a huge rewrite.
## Quick Start
```html
<!-- Load from unpkg -->
<script src="https://unpkg.com/kutty.org@0.0.2"></script>
<script src="https://unpkg.com/htmx.org@0.0.2"></script>
<!-- have a button POST a click via AJAX -->
<button kt-post="/clicked" kt-swap="outerHTML">
<button hx-post="/clicked" hx-swap="outerHTML">
Click Me
</button>
```
The `kt-post` and `kt-swap` attributes tell kutty:
The `hx-post` and `hx-swap` attributes tell htmx:
> "When a user clicks on this button, issue an AJAX request to /example, and replace the entire button with the response"
@ -30,6 +30,6 @@ Kutty is based on [intercooler.js](http://intercoolerjs.org) and is the successo
## Website & Docs
[https://kutty.org](https://kutty.org)
[https://htmx.org](https://htmx.org)
[https://kutty.org/docs](https://kutty.org/docs)
[https://htmx.org/docs](https://htmx.org/docs)

12
TODO.md
View File

@ -10,9 +10,9 @@
* Testing
* interval parsing
* scrolling/'revealed' event
* kt-swap-oob (verify, chrome coverage tool bad?)
* hx-swap-oob (verify, chrome coverage tool bad?)
* SSE stuff
* kt-trigger delay
* hx-trigger delay
* class operation parsing
* class toggling
* transition model for content swaps
@ -21,11 +21,11 @@
* logrithmic back off on history cache size on QuotaExceededError
* ctrl-click on boosted anchors: tab opens normally
* `kutty-on="myEvent: ...""` attribute for handling custom events
* `kutty-requests` class on body
* local references (e.g. kt-get="#foo")
* `htmx-on="myEvent: ...""` attribute for handling custom events
* `htmx-requests` class on body
* local references (e.g. hx-get="#foo")
* focus recapture
* Move to weakmap for kutty node info? https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap
* Move to weakmap for htmx node info? https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap
* Scroll handler use https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API if available?
## Unsupported Intercooler Features

View File

@ -1,5 +1,5 @@
// noinspection JSUnusedAssignment
var kutty = kutty || (function () {
var htmx = kutty || (function () {
'use strict';
var VERBS = ['get', 'post', 'put', 'delete', 'patch']
@ -285,9 +285,9 @@ var kutty = kutty || (function () {
//====================================================================
function getTarget(elt) {
var explicitTarget = getClosestMatch(elt, function(e){return getRawAttribute(e,"kt-target") !== null});
var explicitTarget = getClosestMatch(elt, function(e){return getRawAttribute(e,"hx-target") !== null});
if (explicitTarget) {
var targetStr = getRawAttribute(explicitTarget, "kt-target");
var targetStr = getRawAttribute(explicitTarget, "hx-target");
if (targetStr === "this") {
return explicitTarget;
} else if (targetStr.indexOf("closest ") === 0) {
@ -319,7 +319,7 @@ var kutty = kutty || (function () {
function handleOutOfBandSwaps(fragment) {
var settleTasks = [];
forEach(fragment.children, function (child) {
if (getAttributeValue(child, "kt-swap-oob") === "true") {
if (getAttributeValue(child, "hx-swap-oob") === "true") {
var target = getDocument().getElementById(child.id);
if (target) {
var fragment = getDocument().createDocumentFragment();
@ -401,7 +401,7 @@ var kutty = kutty || (function () {
}
function maybeSelectFromResponse(elt, fragment) {
var selector = getClosestAttributeValue(elt, "kt-select");
var selector = getClosestAttributeValue(elt, "hx-select");
if (selector) {
var newFragment = getDocument().createDocumentFragment();
forEach(fragment.querySelectorAll(selector), function (node) {
@ -454,7 +454,7 @@ var kutty = kutty || (function () {
var triggerSpec = {
"trigger" : "click"
}
var explicitTrigger = getAttributeValue(elt, 'kt-trigger');
var explicitTrigger = getAttributeValue(elt, 'hx-trigger');
if (explicitTrigger) {
var tokens = splitOnWhitespace(explicitTrigger);
if (tokens.length > 0) {
@ -548,7 +548,7 @@ var kutty = kutty || (function () {
nodeData.timeout = setTimeout(function () {
if (bodyContains(elt) && nodeData.cancelled !== true) {
issueAjaxRequest(elt, verb, path);
processPolling(elt, verb, getAttributeValue(elt, "kt-" + verb), interval);
processPolling(elt, verb, getAttributeValue(elt, "hx-" + verb), interval);
}
}, interval);
}
@ -623,7 +623,7 @@ var kutty = kutty || (function () {
function initScrollHandler() {
if (!window['kuttyScrollHandler']) {
var scrollHandler = function() {
forEach(getDocument().querySelectorAll("[kt-trigger='revealed']"), function (elt) {
forEach(getDocument().querySelectorAll("[hx-trigger='revealed']"), function (elt) {
maybeReveal(elt);
});
};
@ -697,7 +697,7 @@ var kutty = kutty || (function () {
function processVerbs(elt, nodeData, triggerSpec) {
var explicitAction = false;
forEach(VERBS, function (verb) {
var path = getAttributeValue(elt, 'kt-' + verb);
var path = getAttributeValue(elt, 'hx-' + verb);
if (path) {
explicitAction = true;
nodeData.path = path;
@ -728,14 +728,14 @@ var kutty = kutty || (function () {
var triggerSpec = getTriggerSpec(elt);
var explicitAction = processVerbs(elt, nodeData, triggerSpec);
if (!explicitAction && getClosestAttributeValue(elt, "kt-boost") === "true") {
if (!explicitAction && getClosestAttributeValue(elt, "hx-boost") === "true") {
boostElement(elt, nodeData, triggerSpec);
}
var sseSrc = getAttributeValue(elt, 'kt-sse-source');
var sseSrc = getAttributeValue(elt, 'hx-sse-source');
if (sseSrc) {
initSSESource(elt, sseSrc);
}
var addClass = getAttributeValue(elt, 'kt-classes');
var addClass = getAttributeValue(elt, 'hx-classes');
if (addClass) {
processClassList(elt, addClass);
}
@ -750,7 +750,7 @@ var kutty = kutty || (function () {
//====================================================================
function sendError(elt, eventName, detail) {
var errorURL = getClosestAttributeValue(elt, "kt-error-url");
var errorURL = getClosestAttributeValue(elt, "hx-error-url");
if (errorURL) {
var xhr = new XMLHttpRequest();
xhr.open("POST", errorURL);
@ -793,7 +793,7 @@ var kutty = kutty || (function () {
var currentPathForHistory = null;
function getHistoryElement() {
var historyElt = getDocument().querySelector('[kt-history-elt]');
var historyElt = getDocument().querySelector('[hx-history-elt]');
return historyElt || getDocument().body;
}
@ -850,7 +850,7 @@ var kutty = kutty || (function () {
if (this.status >= 200 && this.status < 400) {
triggerEvent(getDocument().body, "historyCacheMissLoad.kutty", details);
var fragment = makeFragment(this.response);
fragment = fragment.querySelector('[kt-history-elt]') || fragment;
fragment = fragment.querySelector('[hx-history-elt]') || fragment;
settleImmediately(swapInnerHTML(getHistoryElement(), fragment));
currentPathForHistory = path;
} else {
@ -876,7 +876,7 @@ var kutty = kutty || (function () {
}
function shouldPush(elt) {
return getClosestAttributeValue(elt, "kt-push-url") === "true" ||
return getClosestAttributeValue(elt, "hx-push-url") === "true" ||
(elt.tagName === "A" && getInternalData(elt).boosted);
}
@ -889,7 +889,7 @@ var kutty = kutty || (function () {
}
function mutateRequestIndicatorClasses(elt, action) {
var indicator = getClosestAttributeValue(elt, 'kt-indicator');
var indicator = getClosestAttributeValue(elt, 'hx-indicator');
if (indicator) {
var indicators = getDocument().querySelectorAll(indicator);
} else {
@ -965,7 +965,7 @@ var kutty = kutty || (function () {
processInputValue(processed, values, elt);
// include any explicit includes
var includes = getClosestAttributeValue(elt, "kt-include");
var includes = getClosestAttributeValue(elt, "hx-include");
if (includes) {
var nodes = getDocument().querySelectorAll(includes);
forEach(nodes, function(node) {
@ -1034,7 +1034,7 @@ var kutty = kutty || (function () {
}
function filterValues(inputValues, elt, verb) {
var paramsValue = getClosestAttributeValue(elt, "kt-params");
var paramsValue = getClosestAttributeValue(elt, "hx-params");
if (paramsValue) {
if (paramsValue === "none") {
return {};
@ -1060,7 +1060,7 @@ var kutty = kutty || (function () {
}
function getSwapSpecification(elt) {
var swapInfo = getClosestAttributeValue(elt, "kt-swap");
var swapInfo = getClosestAttributeValue(elt, "hx-swap");
var swapSpec = {
"swapStyle" : kutty.config.defaultSwapStyle,
"swapDelay" : kutty.config.defaultSwapDelay,
@ -1087,7 +1087,7 @@ var kutty = kutty || (function () {
function issueAjaxRequest(elt, verb, path, eventTarget) {
var target = getTarget(elt);
if (target == null) {
triggerErrorEvent(elt, 'targetError.kutty', {target: getRawAttribute(elt, "kt-target")});
triggerErrorEvent(elt, 'targetError.kutty', {target: getRawAttribute(elt, "hx-target")});
return;
}
var eltData = getInternalData(elt);
@ -1099,13 +1099,13 @@ var kutty = kutty || (function () {
var endRequestLock = function(){
eltData.requestInFlight = false
}
var promptQuestion = getClosestAttributeValue(elt, "kt-prompt");
var promptQuestion = getClosestAttributeValue(elt, "hx-prompt");
if (promptQuestion) {
var prompt = prompt(promptQuestion);
if(!triggerEvent(elt, 'prompt.kutty', {prompt: prompt, target:target})) return endRequestLock();
}
var confirmQuestion = getClosestAttributeValue(elt, "kt-confirm");
var confirmQuestion = getClosestAttributeValue(elt, "hx-confirm");
if (confirmQuestion) {
if(!confirm(confirmQuestion)) return endRequestLock();
}

File diff suppressed because one or more lines are too long

2
package-lock.json generated
View File

@ -1,5 +1,5 @@
{
"name": "kutty.org",
"name": "htmx.org",
"version": "0.0.1",
"lockfileVersion": 1,
"requires": true,

View File

@ -1,14 +1,14 @@
{
"name": "kutty.org",
"name": "htmx.org",
"description": "high power tools for html",
"keywords": [
"AJAX",
"HTML"
],
"version": "0.0.2",
"homepage": "https://kutty.org/",
"version": "0.0.3",
"homepage": "https://htmx.org/",
"bugs": {
"url": "https://github.com/bigskysoftware/kutty/issues"
"url": "https://github.com/bigskysoftware/htmx/issues"
},
"license": "BSD 2-Clause",
"files": [
@ -17,17 +17,17 @@
"dist/*.js",
"dist/*.js.gz"
],
"main": "dist/kutty.min.js",
"unpkg": "dist/kutty.min.js",
"main": "dist/htmx.min.js",
"unpkg": "dist/htmx.min.js",
"scripts": {
"test": "mocha-chrome test/index.html",
"dist": "cp src/kutty.js dist/ && npm run-script uglify && gzip -k -f dist/kutty.min.js > dist/kutty.min.js.gz && exit",
"dist": "cp src/htmx.js dist/ && npm run-script uglify && gzip -k -f dist/htmx.min.js > dist/htmx.min.js.gz && exit",
"www": "node scripts/www.js",
"uglify": "uglifyjs -m eval -o dist/kutty.min.js dist/kutty.js"
"uglify": "uglifyjs -m eval -o dist/htmx.min.js dist/htmx.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/bigskysoftware/kutty.git"
"url": "git+https://github.com/bigskysoftware/htmx.git"
},
"devDependencies": {
"@11ty/eleventy": "^0.10.0",

View File

@ -11,4 +11,4 @@ fs.copySync("node_modules/chai/chai.js", testRoot + "/node_modules/chai/chai.js"
fs.copySync("node_modules/sinon/pkg/sinon.js", testRoot + "/node_modules/sinon/pkg/sinon.js");
fs.copySync("test/", testRoot + "/test");
fs.copySync("src/", testRoot + "/src");
fs.copySync("src/kutty.js", "www/js/kutty.js");
fs.copySync("src/htmx.js", "www/js/htmx.js");

View File

@ -1,5 +1,5 @@
// noinspection JSUnusedAssignment
var kutty = kutty || (function () {
var htmx = htmx || (function () {
'use strict';
var VERBS = ['get', 'post', 'put', 'delete', 'patch']
@ -118,7 +118,7 @@ var kutty = kutty || (function () {
}
function getInternalData(elt) {
var dataProp = 'kutty-internal-data';
var dataProp = 'htmx-internal-data';
var data = elt[dataProp];
if (!data) {
data = elt[dataProp] = {};
@ -181,14 +181,14 @@ var kutty = kutty || (function () {
}
function onLoadHelper(callback) {
var value = kutty.on("load.kutty", function(evt) {
var value = htmx.on("load.htmx", function(evt) {
callback(evt.detail.elt);
});
return value;
}
function logAll(){
kutty.logger = function(elt, event, data) {
htmx.logger = function(elt, event, data) {
if(console) {
console.log(event, elt, data);
}
@ -290,9 +290,9 @@ var kutty = kutty || (function () {
//====================================================================
function getTarget(elt) {
var explicitTarget = getClosestMatch(elt, function(e){return getRawAttribute(e,"kt-target") !== null});
var explicitTarget = getClosestMatch(elt, function(e){return getRawAttribute(e,"hx-target") !== null});
if (explicitTarget) {
var targetStr = getRawAttribute(explicitTarget, "kt-target");
var targetStr = getRawAttribute(explicitTarget, "hx-target");
if (targetStr === "this") {
return explicitTarget;
} else if (targetStr.indexOf("closest ") === 0) {
@ -324,7 +324,7 @@ var kutty = kutty || (function () {
function handleOutOfBandSwaps(fragment) {
var settleTasks = [];
forEach(toArray(fragment.children), function (child) {
if (getAttributeValue(child, "kt-swap-oob") === "true") {
if (getAttributeValue(child, "hx-swap-oob") === "true") {
var target = getDocument().getElementById(child.id);
if (target) {
var fragment = getDocument().createDocumentFragment();
@ -332,7 +332,7 @@ var kutty = kutty || (function () {
settleTasks = settleTasks.concat(swapOuterHTML(target, fragment));
} else {
child.parentNode.removeChild(child);
triggerErrorEvent(getDocument().body, "oobErrorNoTarget.kutty", {content: child})
triggerErrorEvent(getDocument().body, "oobErrorNoTarget.htmx", {content: child})
}
}
});
@ -360,7 +360,7 @@ var kutty = kutty || (function () {
var child = fragment.firstChild;
parentNode.insertBefore(child, insertBefore);
if (child.nodeType !== Node.TEXT_NODE) {
triggerEvent(child, 'load.kutty', {});
triggerEvent(child, 'load.htmx', {});
processNode(child);
}
}
@ -406,7 +406,7 @@ var kutty = kutty || (function () {
}
function maybeSelectFromResponse(elt, fragment) {
var selector = getClosestAttributeValue(elt, "kt-select");
var selector = getClosestAttributeValue(elt, "hx-select");
if (selector) {
var newFragment = getDocument().createDocumentFragment();
forEach(fragment.querySelectorAll(selector), function (node) {
@ -459,7 +459,7 @@ var kutty = kutty || (function () {
var triggerSpec = {
"trigger" : "click"
}
var explicitTrigger = getAttributeValue(elt, 'kt-trigger');
var explicitTrigger = getAttributeValue(elt, 'hx-trigger');
if (explicitTrigger) {
var tokens = splitOnWhitespace(explicitTrigger);
if (tokens.length > 0) {
@ -553,7 +553,7 @@ var kutty = kutty || (function () {
nodeData.timeout = setTimeout(function () {
if (bodyContains(elt) && nodeData.cancelled !== true) {
issueAjaxRequest(elt, verb, path);
processPolling(elt, verb, getAttributeValue(elt, "kt-" + verb), interval);
processPolling(elt, verb, getAttributeValue(elt, "hx-" + verb), interval);
}
}, interval);
}
@ -626,13 +626,13 @@ var kutty = kutty || (function () {
}
function initScrollHandler() {
if (!window['kuttyScrollHandler']) {
if (!window['htmxScrollHandler']) {
var scrollHandler = function() {
forEach(getDocument().querySelectorAll("[kt-trigger='revealed']"), function (elt) {
forEach(getDocument().querySelectorAll("[hx-trigger='revealed']"), function (elt) {
maybeReveal(elt);
});
};
window['kuttyScrollHandler'] = scrollHandler;
window['htmxScrollHandler'] = scrollHandler;
window.addEventListener("scroll", scrollHandler)
}
}
@ -656,10 +656,10 @@ var kutty = kutty || (function () {
var detail = {
config:{withCredentials: true}
};
triggerEvent(elt, "initSSE.kutty", detail);
triggerEvent(elt, "initSSE.htmx", detail);
var source = new EventSource(sseSrc, detail.config);
source.onerror = function (e) {
triggerErrorEvent(elt, "sseError.kutty", {error:e, source:source});
triggerErrorEvent(elt, "sseError.htmx", {error:e, source:source});
maybeCloseSSESource(elt);
};
getInternalData(elt).sseSource = source;
@ -681,7 +681,7 @@ var kutty = kutty || (function () {
};
sseSource.sseSource.addEventListener(sseEventName, sseListener);
} else {
triggerErrorEvent(elt, "noSSESourceError.kutty")
triggerErrorEvent(elt, "noSSESourceError.htmx")
}
}
@ -702,7 +702,7 @@ var kutty = kutty || (function () {
function processVerbs(elt, nodeData, triggerSpec) {
var explicitAction = false;
forEach(VERBS, function (verb) {
var path = getAttributeValue(elt, 'kt-' + verb);
var path = getAttributeValue(elt, 'hx-' + verb);
if (path) {
explicitAction = true;
nodeData.path = path;
@ -733,14 +733,14 @@ var kutty = kutty || (function () {
var triggerSpec = getTriggerSpec(elt);
var explicitAction = processVerbs(elt, nodeData, triggerSpec);
if (!explicitAction && getClosestAttributeValue(elt, "kt-boost") === "true") {
if (!explicitAction && getClosestAttributeValue(elt, "hx-boost") === "true") {
boostElement(elt, nodeData, triggerSpec);
}
var sseSrc = getAttributeValue(elt, 'kt-sse-source');
var sseSrc = getAttributeValue(elt, 'hx-sse-source');
if (sseSrc) {
initSSESource(elt, sseSrc);
}
var addClass = getAttributeValue(elt, 'kt-classes');
var addClass = getAttributeValue(elt, 'hx-classes');
if (addClass) {
processClassList(elt, addClass);
}
@ -755,7 +755,7 @@ var kutty = kutty || (function () {
//====================================================================
function sendError(elt, eventName, detail) {
var errorURL = getClosestAttributeValue(elt, "kt-error-url");
var errorURL = getClosestAttributeValue(elt, "hx-error-url");
if (errorURL) {
var xhr = new XMLHttpRequest();
xhr.open("POST", errorURL);
@ -782,8 +782,8 @@ var kutty = kutty || (function () {
function triggerEvent(elt, eventName, detail) {
detail["elt"] = elt;
var event = makeEvent(eventName, detail);
if (kutty.logger) {
kutty.logger(elt, eventName, detail);
if (htmx.logger) {
htmx.logger(elt, eventName, detail);
if (detail.isError) {
sendError(elt, eventName, detail);
}
@ -798,12 +798,12 @@ var kutty = kutty || (function () {
var currentPathForHistory = null;
function getHistoryElement() {
var historyElt = getDocument().querySelector('[kt-history-elt]');
var historyElt = getDocument().querySelector('[hx-history-elt]');
return historyElt || getDocument().body;
}
function saveToHistoryCache(url, content, title, scroll) {
var historyCache = JSON.parse(localStorage.getItem("kutty-history-cache")) || [];
var historyCache = JSON.parse(localStorage.getItem("htmx-history-cache")) || [];
for (var i = 0; i < historyCache.length; i++) {
if (historyCache[i].url === url) {
historyCache = historyCache.slice(i, 1);
@ -811,14 +811,14 @@ var kutty = kutty || (function () {
}
}
historyCache.push({url:url, content: content, title:title, scroll:scroll})
while (historyCache.length > kutty.config.historyCacheSize) {
while (historyCache.length > htmx.config.historyCacheSize) {
historyCache.shift();
}
localStorage.setItem("kutty-history-cache", JSON.stringify(historyCache));
localStorage.setItem("htmx-history-cache", JSON.stringify(historyCache));
}
function getCachedHistory(url) {
var historyCache = JSON.parse(localStorage.getItem("kutty-history-cache")) || [];
var historyCache = JSON.parse(localStorage.getItem("htmx-history-cache")) || [];
for (var i = 0; i < historyCache.length; i++) {
if (historyCache[i].url === url) {
return historyCache[i];
@ -830,13 +830,13 @@ var kutty = kutty || (function () {
function saveHistory() {
var elt = getHistoryElement();
var path = currentPathForHistory || location.pathname+location.search;
triggerEvent(getDocument().body, "beforeHistorySave.kutty", {path:path, historyElt:elt});
if(kutty.config.historyEnabled) history.replaceState({}, getDocument().title, window.location.href);
triggerEvent(getDocument().body, "beforeHistorySave.htmx", {path:path, historyElt:elt});
if(htmx.config.historyEnabled) history.replaceState({}, getDocument().title, window.location.href);
saveToHistoryCache(path, elt.innerHTML, getDocument().title, window.scrollY);
}
function pushUrlIntoHistory(path) {
if(kutty.config.historyEnabled) history.pushState({}, "", path);
if(htmx.config.historyEnabled) history.pushState({}, "", path);
currentPathForHistory = path;
}
@ -849,17 +849,17 @@ var kutty = kutty || (function () {
function loadHistoryFromServer(path) {
var request = new XMLHttpRequest();
var details = {path: path, xhr:request};
triggerEvent(getDocument().body, "historyCacheMiss.kutty", details);
triggerEvent(getDocument().body, "historyCacheMiss.htmx", details);
request.open('GET', path, true);
request.onload = function () {
if (this.status >= 200 && this.status < 400) {
triggerEvent(getDocument().body, "historyCacheMissLoad.kutty", details);
triggerEvent(getDocument().body, "historyCacheMissLoad.htmx", details);
var fragment = makeFragment(this.response);
fragment = fragment.querySelector('[kt-history-elt]') || fragment;
fragment = fragment.querySelector('[hx-history-elt]') || fragment;
settleImmediately(swapInnerHTML(getHistoryElement(), fragment));
currentPathForHistory = path;
} else {
triggerErrorEvent(getDocument().body, "historyCacheMissLoadError.kutty", details);
triggerErrorEvent(getDocument().body, "historyCacheMissLoadError.htmx", details);
}
};
request.send();
@ -868,7 +868,7 @@ var kutty = kutty || (function () {
function restoreHistory(path) {
saveHistory(currentPathForHistory);
path = path || location.pathname+location.search;
triggerEvent(getDocument().body, "historyRestore.kutty", {path:path});
triggerEvent(getDocument().body, "historyRestore.htmx", {path:path});
var cached = getCachedHistory(path);
if (cached) {
settleImmediately(swapInnerHTML(getHistoryElement(), makeFragment(cached.content)));
@ -881,7 +881,7 @@ var kutty = kutty || (function () {
}
function shouldPush(elt) {
return getClosestAttributeValue(elt, "kt-push-url") === "true" ||
return getClosestAttributeValue(elt, "hx-push-url") === "true" ||
(elt.tagName === "A" && getInternalData(elt).boosted);
}
@ -894,14 +894,14 @@ var kutty = kutty || (function () {
}
function mutateRequestIndicatorClasses(elt, action) {
var indicator = getClosestAttributeValue(elt, 'kt-indicator');
var indicator = getClosestAttributeValue(elt, 'hx-indicator');
if (indicator) {
var indicators = getDocument().querySelectorAll(indicator);
} else {
indicators = [elt];
}
forEach(indicators, function(ic) {
ic.classList[action].call(ic.classList, "kutty-request");
ic.classList[action].call(ic.classList, "htmx-request");
});
}
@ -970,7 +970,7 @@ var kutty = kutty || (function () {
processInputValue(processed, values, elt);
// include any explicit includes
var includes = getClosestAttributeValue(elt, "kt-include");
var includes = getClosestAttributeValue(elt, "hx-include");
if (includes) {
var nodes = getDocument().querySelectorAll(includes);
forEach(nodes, function(node) {
@ -1016,30 +1016,30 @@ var kutty = kutty || (function () {
function getHeaders(elt, target, prompt, eventTarget) {
var headers = {
"X-KT-Request" : "true",
"X-KT-Trigger" : getRawAttribute(elt, "id"),
"X-KT-Trigger-Name" : getRawAttribute(elt, "name"),
"X-KT-Target" : getRawAttribute(target, "id"),
"X-HX-Request" : "true",
"X-HX-Trigger" : getRawAttribute(elt, "id"),
"X-HX-Trigger-Name" : getRawAttribute(elt, "name"),
"X-HX-Target" : getRawAttribute(target, "id"),
"Current-URL" : getDocument().location.href,
}
if (prompt) {
headers["X-KT-Prompt"] = prompt;
headers["X-HX-Prompt"] = prompt;
}
if (eventTarget) {
headers["X-KT-Event-Target"] = getRawAttribute(eventTarget, "id");
headers["X-HX-Event-Target"] = getRawAttribute(eventTarget, "id");
}
if (getDocument().activeElement) {
headers["X-KT-Active-Element"] = getRawAttribute(getDocument().activeElement, "id");
headers["X-KT-Active-Element-Name"] = getRawAttribute(getDocument().activeElement, "name");
headers["X-HX-Active-Element"] = getRawAttribute(getDocument().activeElement, "id");
headers["X-HX-Active-Element-Name"] = getRawAttribute(getDocument().activeElement, "name");
if (getDocument().activeElement.value) {
headers["X-KT-Active-Element-Value"] = getRawAttribute(getDocument().activeElement, "value");
headers["X-HX-Active-Element-Value"] = getRawAttribute(getDocument().activeElement, "value");
}
}
return headers;
}
function filterValues(inputValues, elt, verb) {
var paramsValue = getClosestAttributeValue(elt, "kt-params");
var paramsValue = getClosestAttributeValue(elt, "hx-params");
if (paramsValue) {
if (paramsValue === "none") {
return {};
@ -1065,11 +1065,11 @@ var kutty = kutty || (function () {
}
function getSwapSpecification(elt) {
var swapInfo = getClosestAttributeValue(elt, "kt-swap");
var swapInfo = getClosestAttributeValue(elt, "hx-swap");
var swapSpec = {
"swapStyle" : kutty.config.defaultSwapStyle,
"swapDelay" : kutty.config.defaultSwapDelay,
"settleDelay" : kutty.config.defaultSettleDelay
"swapStyle" : htmx.config.defaultSwapStyle,
"swapDelay" : htmx.config.defaultSwapDelay,
"settleDelay" : htmx.config.defaultSettleDelay
}
if (swapInfo) {
var split = splitOnWhitespace(swapInfo);
@ -1092,7 +1092,7 @@ var kutty = kutty || (function () {
function issueAjaxRequest(elt, verb, path, eventTarget) {
var target = getTarget(elt);
if (target == null) {
triggerErrorEvent(elt, 'targetError.kutty', {target: getRawAttribute(elt, "kt-target")});
triggerErrorEvent(elt, 'targetError.htmx', {target: getRawAttribute(elt, "hx-target")});
return;
}
var eltData = getInternalData(elt);
@ -1104,13 +1104,13 @@ var kutty = kutty || (function () {
var endRequestLock = function(){
eltData.requestInFlight = false
}
var promptQuestion = getClosestAttributeValue(elt, "kt-prompt");
var promptQuestion = getClosestAttributeValue(elt, "hx-prompt");
if (promptQuestion) {
var promptResponse = prompt(promptQuestion);
if(!triggerEvent(elt, 'prompt.kutty', {prompt: promptResponse, target:target})) return endRequestLock();
if(!triggerEvent(elt, 'prompt.htmx', {prompt: promptResponse, target:target})) return endRequestLock();
}
var confirmQuestion = getClosestAttributeValue(elt, "kt-confirm");
var confirmQuestion = getClosestAttributeValue(elt, "hx-confirm");
if (confirmQuestion) {
if(!confirm(confirmQuestion)) return endRequestLock();
}
@ -1135,7 +1135,7 @@ var kutty = kutty || (function () {
target:target,
verb:verb
};
if(!triggerEvent(elt, 'configRequest.kutty', requestConfig)) return endRequestLock();
if(!triggerEvent(elt, 'configRequest.htmx', requestConfig)) return endRequestLock();
// request type
var requestURL;
@ -1160,10 +1160,10 @@ var kutty = kutty || (function () {
var eventDetail = {xhr: xhr, target: target};
xhr.onload = function () {
try {
if (!triggerEvent(elt, 'beforeOnLoad.kutty', eventDetail)) return;
if (!triggerEvent(elt, 'beforeOnLoad.htmx', eventDetail)) return;
handleTrigger(elt, this.getResponseHeader("X-KT-Trigger"));
var pushedUrl = this.getResponseHeader("X-KT-Push");
handleTrigger(elt, this.getResponseHeader("X-HX-Trigger"));
var pushedUrl = this.getResponseHeader("X-HX-Push");
var shouldSaveHistory = shouldPush(elt) || pushedUrl;
@ -1173,7 +1173,7 @@ var kutty = kutty || (function () {
}
// don't process 'No Content' response
if (this.status !== 204) {
if (!triggerEvent(elt, 'beforeSwap.kutty', eventDetail)) return;
if (!triggerEvent(elt, 'beforeSwap.htmx', eventDetail)) return;
var resp = this.response;
@ -1184,24 +1184,24 @@ var kutty = kutty || (function () {
var swapSpec = getSwapSpecification(elt);
target.classList.add("kutty-swapping");
target.classList.add("htmx-swapping");
var doSwap = function () {
try {
var settleTasks = swapResponse(swapSpec.swapStyle, target, elt, resp);
target.classList.remove("kutty-swapping");
target.classList.add("kutty-settling");
triggerEvent(elt, 'afterSwap.kutty', eventDetail);
target.classList.remove("htmx-swapping");
target.classList.add("htmx-settling");
triggerEvent(elt, 'afterSwap.htmx', eventDetail);
var doSettle = function(){
forEach(settleTasks, function (settleTask) {
settleTask.call();
});
target.classList.remove("kutty-settling");
target.classList.remove("htmx-settling");
// push URL and save new page
if (shouldSaveHistory) {
pushUrlIntoHistory(pushedUrl || requestURL );
}
triggerEvent(elt, 'afterSettle.kutty', eventDetail);
triggerEvent(elt, 'afterSettle.htmx', eventDetail);
}
if (swapSpec.settleDelay > 0) {
@ -1210,7 +1210,7 @@ var kutty = kutty || (function () {
doSettle();
}
} catch (e) {
triggerErrorEvent(elt, 'swapError.kutty', eventDetail);
triggerErrorEvent(elt, 'swapError.htmx', eventDetail);
throw e;
}
};
@ -1222,24 +1222,24 @@ var kutty = kutty || (function () {
}
}
} else {
triggerErrorEvent(elt, 'responseError.kutty', eventDetail);
triggerErrorEvent(elt, 'responseError.htmx', eventDetail);
}
} catch (e) {
eventDetail['exception'] = e;
triggerErrorEvent(elt, 'onLoadError.kutty', eventDetail);
triggerErrorEvent(elt, 'onLoadError.htmx', eventDetail);
throw e;
} finally {
removeRequestIndicatorClasses(elt);
endRequestLock();
triggerEvent(elt, 'afterOnLoad.kutty', eventDetail);
triggerEvent(elt, 'afterOnLoad.htmx', eventDetail);
}
}
xhr.onerror = function () {
removeRequestIndicatorClasses(elt);
triggerErrorEvent(elt, 'sendError.kutty', eventDetail);
triggerErrorEvent(elt, 'sendError.htmx', eventDetail);
endRequestLock();
}
if(!triggerEvent(elt, 'beforeRequest.kutty', eventDetail)) return endRequestLock();
if(!triggerEvent(elt, 'beforeRequest.htmx', eventDetail)) return endRequestLock();
addRequestIndicatorClasses(elt);
xhr.send(verb === 'get' ? null : urlEncode(filteredParameters));
}
@ -1256,21 +1256,21 @@ var kutty = kutty || (function () {
}
}
// insert kutty-indicator css rules immediate, if not configured otherwise
// insert htmx-indicator css rules immediate, if not configured otherwise
(function() {
var metaConfig = getMetaConfig();
if (metaConfig === null || metaConfig.includeIndicatorStyles !== false) {
getDocument().head.insertAdjacentHTML("beforeend",
"<style>\
.kutty-indicator{opacity:0;transition: opacity 200ms ease-in;}\
.kutty-request .kutty-indicator{opacity:1}\
.kutty-request.kutty-indicator{opacity:1}\
.htmx-indicator{opacity:0;transition: opacity 200ms ease-in;}\
.htmx-request .htmx-indicator{opacity:1}\
.htmx-request.htmx-indicator{opacity:1}\
</style>");
}
})();
function getMetaConfig() {
var element = getDocument().querySelector('meta[name="kutty-config"]');
var element = getDocument().querySelector('meta[name="htmx-config"]');
if (element) {
return JSON.parse(element.content);
} else {
@ -1281,7 +1281,7 @@ var kutty = kutty || (function () {
function mergeMetaConfig() {
var metaConfig = getMetaConfig();
if (metaConfig) {
kutty.config = mergeObjects(kutty.config , metaConfig)
htmx.config = mergeObjects(htmx.config , metaConfig)
}
}
@ -1290,7 +1290,7 @@ var kutty = kutty || (function () {
mergeMetaConfig();
var body = getDocument().body;
processNode(body);
triggerEvent(body, 'load.kutty', {});
triggerEvent(body, 'load.htmx', {});
window.onpopstate = function () {
restoreHistory();
};

View File

@ -1,4 +1,4 @@
describe("kt-boost attribute", function() {
describe("hx-boost attribute", function() {
beforeEach(function () {
this.server = makeServer();
@ -11,7 +11,7 @@ describe("kt-boost attribute", function() {
it('handles basic anchor properly', function () {
this.server.respondWith("GET", "/test", "Boosted");
var div = make('<div kt-target="this" kt-boost="true"><a id="a1" href="/test">Foo</a></div>');
var div = make('<div hx-target="this" hx-boost="true"><a id="a1" href="/test">Foo</a></div>');
var a = byId('a1');
a.click();
this.server.respond();
@ -22,7 +22,7 @@ describe("kt-boost attribute", function() {
it('handles basic form post properly', function () {
this.server.respondWith("POST", "/test", "Boosted");
this.server.respondWith("POST", "/test", "Boosted");
var div = make('<div kt-target="this" kt-boost="true"><form id="f1" action="/test" method="post"><button id="b1">Submit</button></form></div>');
var div = make('<div hx-target="this" hx-boost="true"><form id="f1" action="/test" method="post"><button id="b1">Submit</button></form></div>');
var btn = byId('b1');
btn.click();
this.server.respond();
@ -31,7 +31,7 @@ describe("kt-boost attribute", function() {
it('handles basic form get properly', function () {
this.server.respondWith("GET", "/test", "Boosted");
var div = make('<div kt-target="this" kt-boost="true"><form id="f1" action="/test" method="get"><button id="b1">Submit</button></form></div>');
var div = make('<div hx-target="this" hx-boost="true"><form id="f1" action="/test" method="get"><button id="b1">Submit</button></form></div>');
var btn = byId('b1');
btn.click();
this.server.respond();
@ -40,7 +40,7 @@ describe("kt-boost attribute", function() {
it('handles basic form with no explicit method property', function () {
this.server.respondWith("GET", "/test", "Boosted");
var div = make('<div kt-target="this" kt-boost="true"><form id="f1" action="/test"><button id="b1">Submit</button></form></div>');
var div = make('<div hx-target="this" hx-boost="true"><form id="f1" action="/test"><button id="b1">Submit</button></form></div>');
var btn = byId('b1');
btn.click();
this.server.respond();

View File

@ -1,4 +1,4 @@
describe("kt-classes attribute", function(){
describe("hx-classes attribute", function(){
beforeEach(function() {
this.server = makeServer();
clearWorkArea();
@ -10,7 +10,7 @@ describe("kt-classes attribute", function(){
it('adds classes properly', function(done)
{
var div = make('<div kt-classes="add c1">Click Me!</div>')
var div = make('<div hx-classes="add c1">Click Me!</div>')
should.equal(div.classList.length, 0);
setTimeout(function(){
should.equal(div.classList.contains("c1"), true);
@ -20,7 +20,7 @@ describe("kt-classes attribute", function(){
it('removes classes properly', function(done)
{
var div = make('<div class="foo bar" kt-classes="remove bar">Click Me!</div>')
var div = make('<div class="foo bar" hx-classes="remove bar">Click Me!</div>')
should.equal(div.classList.contains("foo"), true);
should.equal(div.classList.contains("bar"), true);
setTimeout(function(){

View File

@ -1,4 +1,4 @@
describe("kt-delete attribute", function(){
describe("hx-delete attribute", function(){
beforeEach(function() {
this.server = makeServer();
clearWorkArea();
@ -15,7 +15,7 @@ describe("kt-delete attribute", function(){
xhr.respond(200, {}, "Deleted!");
});
var btn = make('<button kt-delete="/test">Click Me!</button>')
var btn = make('<button hx-delete="/test">Click Me!</button>')
btn.click();
this.server.respond();
btn.innerHTML.should.equal("Deleted!");

View File

@ -1,4 +1,4 @@
describe("kt-error-url attribute", function(){
describe("hx-error-url attribute", function(){
beforeEach(function() {
this.server = makeServer();
clearWorkArea();
@ -13,7 +13,7 @@ describe("kt-error-url attribute", function(){
this.server.respondWith("POST", "/error", function(xhr){
should.equal(JSON.parse(xhr.requestBody).detail.xhr.status, 404);
});
var btn = make('<button kt-error-url="/error" kt-get="/bad">Click Me!</button>')
var btn = make('<button hx-error-url="/error" hx-get="/bad">Click Me!</button>')
btn.click();
this.server.respond();
this.server.respond();

View File

@ -1,4 +1,4 @@
describe("kt-get attribute", function() {
describe("hx-get attribute", function() {
beforeEach(function () {
this.server = makeServer();
clearWorkArea();
@ -11,7 +11,7 @@ describe("kt-get attribute", function() {
it('issues a GET request on click and swaps content', function () {
this.server.respondWith("GET", "/test", "Clicked!");
var btn = make('<button kt-get="/test">Click Me!</button>')
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click();
this.server.respond();
btn.innerHTML.should.equal("Clicked!");
@ -21,7 +21,7 @@ describe("kt-get attribute", function() {
this.server.respondWith("GET", "/test", function (xhr) {
xhr.respond(200, {}, "Clicked!");
});
make('<form><input name="i1" value="value"/><button id="b1" kt-get="/test">Click Me!</inputbutton></form>')
make('<form><input name="i1" value="value"/><button id="b1" hx-get="/test">Click Me!</inputbutton></form>')
var btn = byId("b1");
btn.click();
this.server.respond();

View File

@ -1,4 +1,4 @@
describe("kt-include attribute", function() {
describe("hx-include attribute", function() {
beforeEach(function () {
this.server = makeServer();
clearWorkArea();
@ -14,7 +14,7 @@ describe("kt-include attribute", function() {
params['i1'].should.equal("test");
xhr.respond(200, {}, "Clicked!")
});
var div = make('<div kt-target="this"><input kt-post="/include" kt-trigger="click" id="i1" name="i1" value="test"/></div>')
var div = make('<div hx-target="this"><input hx-post="/include" hx-trigger="click" id="i1" name="i1" value="test"/></div>')
var input = byId("i1")
input.click();
this.server.respond();
@ -27,7 +27,7 @@ describe("kt-include attribute", function() {
params['i1'].should.equal("test");
xhr.respond(200, {}, "Clicked!")
});
var div = make('<form kt-target="this"><div id="d1" kt-post="/include"></div><input name="i1" value="test"/></form>')
var div = make('<form hx-target="this"><div id="d1" hx-post="/include"></div><input name="i1" value="test"/></form>')
var input = byId("d1")
input.click();
this.server.respond();
@ -40,7 +40,7 @@ describe("kt-include attribute", function() {
should.equal(params['i1'], undefined);
xhr.respond(200, {}, "Clicked!")
});
var div = make('<form kt-target="this"><div id="d1" kt-get="/include"></div><input name="i1" value="test"/></form>')
var div = make('<form hx-target="this"><div id="d1" hx-get="/include"></div><input name="i1" value="test"/></form>')
var input = byId("d1")
input.click();
this.server.respond();
@ -53,7 +53,7 @@ describe("kt-include attribute", function() {
params['i1'].should.equal("test");
xhr.respond(200, {}, "Clicked!")
});
var div = make('<form kt-target="this"><input kt-post="/include" kt-trigger="click" id="i1" name="i1" value="test"/></form>')
var div = make('<form hx-target="this"><input hx-post="/include" hx-trigger="click" id="i1" name="i1" value="test"/></form>')
var input = byId("i1")
input.click();
this.server.respond();
@ -66,8 +66,8 @@ describe("kt-include attribute", function() {
params['i1'].should.deep.equal(["test", "test2"]);
xhr.respond(200, {}, "Clicked!")
});
var div = make('<form kt-target="this">' +
'<input kt-post="/include" kt-trigger="click" id="i1" name="i1" value="test"/>' +
var div = make('<form hx-target="this">' +
'<input hx-post="/include" hx-trigger="click" id="i1" name="i1" value="test"/>' +
'<input name="i1" value="test2"/>' +
'</form>')
var input = byId("i1")
@ -82,8 +82,8 @@ describe("kt-include attribute", function() {
params['i1'].should.equal("test");
xhr.respond(200, {}, "Clicked!")
});
var div = make('<form id="f1" kt-target="this">' +
'<input kt-include="#f1" kt-post="/include" kt-trigger="click" id="i1" name="i1" value="test"/>' +
var div = make('<form id="f1" hx-target="this">' +
'<input hx-include="#f1" hx-post="/include" hx-trigger="click" id="i1" name="i1" value="test"/>' +
'</form>')
var input = byId("i1")
input.click();
@ -98,7 +98,7 @@ describe("kt-include attribute", function() {
xhr.respond(200, {}, "Clicked!")
});
make('<input id="i1" name="i1" value="test"/>');
var div = make('<div kt-post="/include" kt-include="#i1"></div>')
var div = make('<div hx-post="/include" hx-include="#i1"></div>')
div.click();
this.server.respond();
div.innerHTML.should.equal("Clicked!");
@ -113,7 +113,7 @@ describe("kt-include attribute", function() {
});
make('<input id="i1" name="i1" value="test"/>');
make('<input id="i2" name="i2" value="test"/>');
var div = make('<div kt-post="/include" kt-include="#i1, #i2"></div>')
var div = make('<div hx-post="/include" hx-include="#i1, #i2"></div>')
div.click();
this.server.respond();
div.innerHTML.should.equal("Clicked!");
@ -130,7 +130,7 @@ describe("kt-include attribute", function() {
'<input name="i1" value="test"/>' +
'<input name="i2" value="test"/>' +
'</form> ');
var div = make('<div kt-post="/include" kt-include="#f1"></div>')
var div = make('<div hx-post="/include" hx-include="#f1"></div>')
div.click();
this.server.respond();
div.innerHTML.should.equal("Clicked!");

View File

@ -1,4 +1,4 @@
describe("kt-indicator attribute", function(){
describe("hx-indicator attribute", function(){
beforeEach(function() {
this.server = sinon.fakeServer.create();
clearWorkArea();
@ -11,26 +11,26 @@ describe("kt-indicator attribute", function(){
it('Indicator classes are properly put on element with no explicit indicator', function()
{
this.server.respondWith("GET", "/test", "Clicked!");
var btn = make('<button kt-get="/test">Click Me!</button>')
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click();
btn.classList.contains("kutty-request").should.equal(true);
btn.classList.contains("htmx-request").should.equal(true);
this.server.respond();
btn.classList.contains("kutty-request").should.equal(false);
btn.classList.contains("htmx-request").should.equal(false);
});
it('Indicator classes are properly put on element with explicit indicator', function()
{
this.server.respondWith("GET", "/test", "Clicked!");
var btn = make('<button kt-get="/test" kt-indicator="#a1, #a2">Click Me!</button>')
var btn = make('<button hx-get="/test" hx-indicator="#a1, #a2">Click Me!</button>')
var a1 = make('<a id="a1"></a>')
var a2 = make('<a id="a2"></a>')
btn.click();
btn.classList.contains("kutty-request").should.equal(false);
a1.classList.contains("kutty-request").should.equal(true);
a2.classList.contains("kutty-request").should.equal(true);
btn.classList.contains("htmx-request").should.equal(false);
a1.classList.contains("htmx-request").should.equal(true);
a2.classList.contains("htmx-request").should.equal(true);
this.server.respond();
btn.classList.contains("kutty-request").should.equal(false);
a1.classList.contains("kutty-request").should.equal(false);
a2.classList.contains("kutty-request").should.equal(false);
btn.classList.contains("htmx-request").should.equal(false);
a1.classList.contains("htmx-request").should.equal(false);
a2.classList.contains("htmx-request").should.equal(false);
});
})

View File

@ -1,4 +1,4 @@
describe("kt-params attribute", function() {
describe("hx-params attribute", function() {
beforeEach(function () {
this.server = makeServer();
clearWorkArea();
@ -16,7 +16,7 @@ describe("kt-params attribute", function() {
should.equal(params['i3'], undefined);
xhr.respond(200, {}, "Clicked!")
});
var form = make('<form kt-trigger="click" kt-post="/params" kt-params="none">' +
var form = make('<form hx-trigger="click" hx-post="/params" hx-params="none">' +
'<input name="i1" value="test"/>' +
'<input name="i2" value="test"/>' +
'<input name="i3" value="test"/>' +
@ -34,7 +34,7 @@ describe("kt-params attribute", function() {
should.equal(params['i3'], "test");
xhr.respond(200, {}, "Clicked!")
});
var form = make('<form kt-trigger="click" kt-post="/params" kt-params="*">' +
var form = make('<form hx-trigger="click" hx-post="/params" hx-params="*">' +
'<input name="i1" value="test"/>' +
'<input name="i2" value="test"/>' +
'<input name="i3" value="test"/>' +
@ -52,7 +52,7 @@ describe("kt-params attribute", function() {
should.equal(params['i3'], "test");
xhr.respond(200, {}, "Clicked!")
});
var form = make('<form kt-trigger="click" kt-post="/params" kt-params="i1, i3">' +
var form = make('<form hx-trigger="click" hx-post="/params" hx-params="i1, i3">' +
'<input name="i1" value="test"/>' +
'<input name="i2" value="test"/>' +
'<input name="i3" value="test"/>' +
@ -70,7 +70,7 @@ describe("kt-params attribute", function() {
should.equal(params['i3'], undefined);
xhr.respond(200, {}, "Clicked!")
});
var form = make('<form kt-trigger="click" kt-post="/params" kt-params="not i1, i3">' +
var form = make('<form hx-trigger="click" hx-post="/params" hx-params="not i1, i3">' +
'<input name="i1" value="test"/>' +
'<input name="i2" value="test"/>' +
'<input name="i3" value="test"/>' +

View File

@ -1,4 +1,4 @@
describe("kt-patch attribute", function(){
describe("hx-patch attribute", function(){
beforeEach(function() {
this.server = makeServer();
clearWorkArea();
@ -15,7 +15,7 @@ describe("kt-patch attribute", function(){
xhr.respond(200, {}, "Patched!");
});
var btn = make('<button kt-patch="/test">Click Me!</button>')
var btn = make('<button hx-patch="/test">Click Me!</button>')
btn.click();
this.server.respond();
btn.innerHTML.should.equal("Patched!");

View File

@ -1,4 +1,4 @@
describe("kt-post attribute", function(){
describe("hx-post attribute", function(){
beforeEach(function() {
this.server = makeServer();
clearWorkArea();
@ -15,7 +15,7 @@ describe("kt-post attribute", function(){
xhr.respond(200, {}, "Posted!");
});
var btn = make('<button kt-post="/test">Click Me!</button>')
var btn = make('<button hx-post="/test">Click Me!</button>')
btn.click();
this.server.respond();
btn.innerHTML.should.equal("Posted!");

View File

@ -1,6 +1,6 @@
describe("kt-push-url attribute", function() {
describe("hx-push-url attribute", function() {
var KUTTY_HISTORY_CACHE = "kutty-history-cache";
var KUTTY_HISTORY_CACHE = "htmx-history-cache";
beforeEach(function () {
this.server = makeServer();
clearWorkArea();
@ -15,7 +15,7 @@ describe("kt-push-url attribute", function() {
it("navigation should push an element into the cache ", function () {
this.server.respondWith("GET", "/test", "second");
getWorkArea().innerHTML.should.be.equal("");
var div = make('<div kt-push-url="true" kt-get="/test">first</div>');
var div = make('<div hx-push-url="true" hx-get="/test">first</div>');
div.click();
this.server.respond();
getWorkArea().textContent.should.equal("second")
@ -24,10 +24,10 @@ describe("kt-push-url attribute", function() {
});
it("restore should return old value", function () {
this.server.respondWith("GET", "/test1", '<div id="d2" kt-push-url="true" kt-get="/test2" kt-swap="outerHTML settle:0">test1</div>');
this.server.respondWith("GET", "/test2", '<div id="d3" kt-push-url="true" kt-get="/test3" kt-swap="outerHTML settle:0">test2</div>');
this.server.respondWith("GET", "/test1", '<div id="d2" hx-push-url="true" hx-get="/test2" hx-swap="outerHTML settle:0">test1</div>');
this.server.respondWith("GET", "/test2", '<div id="d3" hx-push-url="true" hx-get="/test3" hx-swap="outerHTML settle:0">test2</div>');
make('<div id="d1" kt-push-url="true" kt-get="/test1" kt-swap="outerHTML settle:0">init</div>');
make('<div id="d1" hx-push-url="true" hx-get="/test1" hx-swap="outerHTML settle:0">init</div>');
byId("d1").click();
this.server.respond();
@ -41,7 +41,7 @@ describe("kt-push-url attribute", function() {
var cache = JSON.parse(localStorage.getItem(KUTTY_HISTORY_CACHE));
cache.length.should.equal(2);
kutty._('restoreHistory')("/test1")
htmx._('restoreHistory')("/test1")
this.server.respond();
getWorkArea().textContent.should.equal("test1")
});
@ -50,10 +50,10 @@ describe("kt-push-url attribute", function() {
var x = 0;
this.server.respondWith("GET", /test.*/, function(xhr){
x++;
xhr.respond(200, {}, '<div id="d1" kt-push-url="true" kt-get="/test' + x + '" kt-swap="outerHTML settle:0"></div>')
xhr.respond(200, {}, '<div id="d1" hx-push-url="true" hx-get="/test' + x + '" hx-swap="outerHTML settle:0"></div>')
});
getWorkArea().innerHTML.should.be.equal("");
make('<div id="d1" kt-push-url="true" kt-get="/test" kt-swap="outerHTML settle:0"></div>');
make('<div id="d1" hx-push-url="true" hx-get="/test" hx-swap="outerHTML settle:0"></div>');
for (var i = 0; i < 20; i++) { // issue 20 requests
byId("d1").click();
this.server.respond();
@ -63,10 +63,10 @@ describe("kt-push-url attribute", function() {
});
it("cache miss should issue another GET", function () {
this.server.respondWith("GET", "/test1", '<div id="d2" kt-push-url="true" kt-get="/test2" kt-swap="outerHTML settle:0">test1</div>');
this.server.respondWith("GET", "/test2", '<div id="d3" kt-push-url="true" kt-get="/test3" kt-swap="outerHTML settle:0">test2</div>');
this.server.respondWith("GET", "/test1", '<div id="d2" hx-push-url="true" hx-get="/test2" hx-swap="outerHTML settle:0">test1</div>');
this.server.respondWith("GET", "/test2", '<div id="d3" hx-push-url="true" hx-get="/test3" hx-swap="outerHTML settle:0">test2</div>');
make('<div id="d1" kt-push-url="true" kt-get="/test1" kt-swap="outerHTML settle:0">init</div>');
make('<div id="d1" hx-push-url="true" hx-get="/test1" hx-swap="outerHTML settle:0">init</div>');
byId("d1").click();
this.server.respond();
@ -81,7 +81,7 @@ describe("kt-push-url attribute", function() {
cache.length.should.equal(2);
localStorage.removeItem(KUTTY_HISTORY_CACHE); // clear cache
kutty._('restoreHistory')("/test1")
htmx._('restoreHistory')("/test1")
this.server.respond();
getWorkArea().textContent.should.equal("test1")
});

View File

@ -1,4 +1,4 @@
describe("kt-put attribute", function(){
describe("hx-put attribute", function(){
beforeEach(function() {
this.server = makeServer();
clearWorkArea();
@ -15,7 +15,7 @@ describe("kt-put attribute", function(){
xhr.respond(200, {}, "Putted!");
});
var btn = make('<button kt-put="/test">Click Me!</button>')
var btn = make('<button hx-put="/test">Click Me!</button>')
btn.click();
this.server.respond();
btn.innerHTML.should.equal("Putted!");

View File

@ -1,4 +1,4 @@
describe("BOOTSTRAP - kutty AJAX Tests", function(){
describe("BOOTSTRAP - htmx AJAX Tests", function(){
beforeEach(function() {
this.server = makeServer();
clearWorkArea();
@ -12,7 +12,7 @@ describe("BOOTSTRAP - kutty AJAX Tests", function(){
{
var i = 1;
this.server.respondWith("GET", "/test", "<div id='d1'>foo</div><div id='d2'>bar</div>");
var div = make('<div kt-get="/test" kt-select="#d1"></div>');
var div = make('<div hx-get="/test" hx-select="#d1"></div>');
div.click();
this.server.respond();
div.innerHTML.should.equal("<div id=\"d1\">foo</div>");
@ -22,7 +22,7 @@ describe("BOOTSTRAP - kutty AJAX Tests", function(){
{
var i = 1;
this.server.respondWith("GET", "/test", "<html><body><div id='d1'>foo</div><div id='d2'>bar</div></body></html>");
var div = make('<div kt-get="/test" kt-select="#d1"></div>');
var div = make('<div hx-get="/test" hx-select="#d1"></div>');
div.click();
this.server.respond();
div.innerHTML.should.equal("<div id=\"d1\">foo</div>");

View File

@ -1,4 +1,4 @@
describe("kt-swap-oob attribute", function () {
describe("hx-swap-oob attribute", function () {
beforeEach(function () {
this.server = makeServer();
clearWorkArea();
@ -9,8 +9,8 @@ describe("kt-swap-oob attribute", function () {
});
it('handles basic response properly', function () {
this.server.respondWith("GET", "/test", "Clicked<div id='d1' kt-swap-oob='true'>Swapped</div>");
var div = make('<div kt-get="/test">click me</div>');
this.server.respondWith("GET", "/test", "Clicked<div id='d1' hx-swap-oob='true'>Swapped</div>");
var div = make('<div hx-get="/test">click me</div>');
make('<div id="d1"></div>');
div.click();
this.server.respond();
@ -19,8 +19,8 @@ describe("kt-swap-oob attribute", function () {
})
it('handles more than one oob swap properly', function () {
this.server.respondWith("GET", "/test", "Clicked<div id='d1' kt-swap-oob='true'>Swapped1</div><div id='d2' kt-swap-oob='true'>Swapped2</div>");
var div = make('<div kt-get="/test">click me</div>');
this.server.respondWith("GET", "/test", "Clicked<div id='d1' hx-swap-oob='true'>Swapped1</div><div id='d2' hx-swap-oob='true'>Swapped2</div>");
var div = make('<div hx-get="/test">click me</div>');
make('<div id="d1"></div>');
make('<div id="d2"></div>');
div.click();
@ -31,8 +31,8 @@ describe("kt-swap-oob attribute", function () {
})
it('handles no id match properly', function () {
this.server.respondWith("GET", "/test", "Clicked<div id='d1' kt-swap-oob='true'>Swapped</div>");
var div = make('<div kt-get="/test">click me</div>');
this.server.respondWith("GET", "/test", "Clicked<div id='d1' hx-swap-oob='true'>Swapped</div>");
var div = make('<div hx-get="/test">click me</div>');
div.click();
this.server.respond();
div.innerText.should.equal("Clicked");

View File

@ -1,4 +1,4 @@
describe("kt-swap attribute", function(){
describe("hx-swap attribute", function(){
beforeEach(function() {
this.server = makeServer();
clearWorkArea();
@ -10,13 +10,13 @@ describe("kt-swap attribute", function(){
it('swap innerHTML properly', function()
{
this.server.respondWith("GET", "/test", '<a kt-get="/test2">Click Me</a>');
this.server.respondWith("GET", "/test", '<a hx-get="/test2">Click Me</a>');
this.server.respondWith("GET", "/test2", "Clicked!");
var div = make('<div kt-get="/test"></div>')
var div = make('<div hx-get="/test"></div>')
div.click();
this.server.respond();
div.innerHTML.should.equal('<a kt-get="/test2">Click Me</a>');
div.innerHTML.should.equal('<a hx-get="/test2">Click Me</a>');
var a = div.querySelector('a');
a.click();
this.server.respond();
@ -25,10 +25,10 @@ describe("kt-swap attribute", function(){
it('swap outerHTML properly', function()
{
this.server.respondWith("GET", "/test", '<a id="a1" kt-get="/test2">Click Me</a>');
this.server.respondWith("GET", "/test", '<a id="a1" hx-get="/test2">Click Me</a>');
this.server.respondWith("GET", "/test2", "Clicked!");
var div = make('<div id="d1" kt-get="/test" kt-swap="outerHTML"></div>')
var div = make('<div id="d1" hx-get="/test" hx-swap="outerHTML"></div>')
div.click();
should.equal(byId("d1"), div);
this.server.respond();
@ -43,11 +43,11 @@ describe("kt-swap attribute", function(){
var i = 0;
this.server.respondWith("GET", "/test", function(xhr){
i++;
xhr.respond(200, {}, '<a id="a' + i + '" kt-get="/test2" kt-swap="innerHTML">' + i + '</a>');
xhr.respond(200, {}, '<a id="a' + i + '" hx-get="/test2" hx-swap="innerHTML">' + i + '</a>');
});
this.server.respondWith("GET", "/test2", "*");
var div = make('<div kt-get="/test" kt-swap="beforebegin">*</div>')
var div = make('<div hx-get="/test" hx-swap="beforebegin">*</div>')
var parent = div.parentElement;
div.click();
this.server.respond();
@ -73,11 +73,11 @@ describe("kt-swap attribute", function(){
var i = 0;
this.server.respondWith("GET", "/test", function(xhr){
i++;
xhr.respond(200, {}, '<a id="a' + i + '" kt-get="/test2" kt-swap="innerHTML">' + i + '</a>');
xhr.respond(200, {}, '<a id="a' + i + '" hx-get="/test2" hx-swap="innerHTML">' + i + '</a>');
});
this.server.respondWith("GET", "/test2", "*");
var div = make('<div kt-get="/test" kt-swap="afterbegin">*</div>')
var div = make('<div hx-get="/test" hx-swap="afterbegin">*</div>')
div.click();
this.server.respond();
div.innerText.should.equal("1*");
@ -100,11 +100,11 @@ describe("kt-swap attribute", function(){
var i = 0;
this.server.respondWith("GET", "/test", function(xhr){
i++;
xhr.respond(200, {}, '<a id="a' + i + '" kt-get="/test2" kt-swap="innerHTML">' + i + '</a>');
xhr.respond(200, {}, '<a id="a' + i + '" hx-get="/test2" hx-swap="innerHTML">' + i + '</a>');
});
this.server.respondWith("GET", "/test2", "*");
var div = make('<div kt-get="/test" kt-swap="afterbegin"></div>')
var div = make('<div hx-get="/test" hx-swap="afterbegin"></div>')
div.click();
this.server.respond();
div.innerText.should.equal("1");
@ -127,11 +127,11 @@ describe("kt-swap attribute", function(){
var i = 0;
this.server.respondWith("GET", "/test", function(xhr){
i++;
xhr.respond(200, {}, '<a id="a' + i + '" kt-get="/test2" kt-swap="innerHTML">' + i + '</a>');
xhr.respond(200, {}, '<a id="a' + i + '" hx-get="/test2" hx-swap="innerHTML">' + i + '</a>');
});
this.server.respondWith("GET", "/test2", "*");
var div = make('<div kt-get="/test" kt-swap="afterend">*</div>')
var div = make('<div hx-get="/test" hx-swap="afterend">*</div>')
var parent = div.parentElement;
div.click();
this.server.respond();
@ -157,11 +157,11 @@ describe("kt-swap attribute", function(){
var i = 0;
this.server.respondWith("GET", "/test", function(xhr){
i++;
xhr.respond(200, {}, '<a id="a' + i + '" kt-get="/test2" kt-swap="innerHTML">' + i + '</a>');
xhr.respond(200, {}, '<a id="a' + i + '" hx-get="/test2" hx-swap="innerHTML">' + i + '</a>');
});
this.server.respondWith("GET", "/test2", "*");
var div = make('<div kt-get="/test" kt-swap="beforeend">*</div>')
var div = make('<div hx-get="/test" hx-swap="beforeend">*</div>')
div.click();
this.server.respond();
div.innerText.should.equal("*1");
@ -184,11 +184,11 @@ describe("kt-swap attribute", function(){
var i = 0;
this.server.respondWith("GET", "/test", function(xhr){
i++;
xhr.respond(200, {}, '<a id="a' + i + '" kt-get="/test2" kt-swap="innerHTML">' + i + '</a>');
xhr.respond(200, {}, '<a id="a' + i + '" hx-get="/test2" hx-swap="innerHTML">' + i + '</a>');
});
this.server.respondWith("GET", "/test2", "*");
var div = make('<div kt-get="/test" kt-swap="beforeend"></div>')
var div = make('<div hx-get="/test" hx-swap="beforeend"></div>')
div.click();
this.server.respond();
div.innerText.should.equal("1");
@ -207,24 +207,24 @@ describe("kt-swap attribute", function(){
});
it('properly parses various swap specifications', function(){
var swapSpec = kutty._("getSwapSpecification"); // internal function for swap spec
var swapSpec = htmx._("getSwapSpecification"); // internal function for swap spec
swapSpec(make("<div/>")).swapStyle.should.equal("innerHTML")
swapSpec(make("<div kt-swap='innerHTML'/>")).swapStyle.should.equal("innerHTML")
swapSpec(make("<div kt-swap='innerHTML'/>")).swapDelay.should.equal(0)
swapSpec(make("<div kt-swap='innerHTML'/>")).settleDelay.should.equal(100)
swapSpec(make("<div kt-swap='innerHTML swap:10'/>")).swapDelay.should.equal(10)
swapSpec(make("<div kt-swap='innerHTML settle:10'/>")).settleDelay.should.equal(10)
swapSpec(make("<div kt-swap='innerHTML swap:10 settle:11'/>")).swapDelay.should.equal(10)
swapSpec(make("<div kt-swap='innerHTML swap:10 settle:11'/>")).settleDelay.should.equal(11)
swapSpec(make("<div kt-swap='innerHTML settle:11 swap:10'/>")).swapDelay.should.equal(10)
swapSpec(make("<div kt-swap='innerHTML settle:11 swap:10'/>")).settleDelay.should.equal(11)
swapSpec(make("<div kt-swap='innerHTML nonsense settle:11 swap:10'/>")).settleDelay.should.equal(11)
swapSpec(make("<div kt-swap='innerHTML nonsense settle:11 swap:10 '/>")).settleDelay.should.equal(11)
swapSpec(make("<div hx-swap='innerHTML'/>")).swapStyle.should.equal("innerHTML")
swapSpec(make("<div hx-swap='innerHTML'/>")).swapDelay.should.equal(0)
swapSpec(make("<div hx-swap='innerHTML'/>")).settleDelay.should.equal(100)
swapSpec(make("<div hx-swap='innerHTML swap:10'/>")).swapDelay.should.equal(10)
swapSpec(make("<div hx-swap='innerHTML settle:10'/>")).settleDelay.should.equal(10)
swapSpec(make("<div hx-swap='innerHTML swap:10 settle:11'/>")).swapDelay.should.equal(10)
swapSpec(make("<div hx-swap='innerHTML swap:10 settle:11'/>")).settleDelay.should.equal(11)
swapSpec(make("<div hx-swap='innerHTML settle:11 swap:10'/>")).swapDelay.should.equal(10)
swapSpec(make("<div hx-swap='innerHTML settle:11 swap:10'/>")).settleDelay.should.equal(11)
swapSpec(make("<div hx-swap='innerHTML nonsense settle:11 swap:10'/>")).settleDelay.should.equal(11)
swapSpec(make("<div hx-swap='innerHTML nonsense settle:11 swap:10 '/>")).settleDelay.should.equal(11)
})
it('works with a swap delay', function(done) {
this.server.respondWith("GET", "/test", "Clicked!");
var div = make("<div kt-get='/test' kt-swap='innerHTML swap:10ms'></div>");
var div = make("<div hx-get='/test' hx-swap='innerHTML swap:10ms'></div>");
div.click();
this.server.respond();
div.innerText.should.equal("");
@ -235,8 +235,8 @@ describe("kt-swap attribute", function(){
});
it('works with a settle delay', function(done) {
this.server.respondWith("GET", "/test", "<div id='d1' class='foo' kt-get='/test' kt-swap='outerHTML settle:10ms'></div>");
var div = make("<div id='d1' kt-get='/test' kt-swap='outerHTML settle:10ms'></div>");
this.server.respondWith("GET", "/test", "<div id='d1' class='foo' hx-get='/test' hx-swap='outerHTML settle:10ms'></div>");
var div = make("<div id='d1' hx-get='/test' hx-swap='outerHTML settle:10ms'></div>");
div.click();
this.server.respond();
div.classList.contains('foo').should.equal(false);

View File

@ -1,4 +1,4 @@
describe("kt-target attribute", function(){
describe("hx-target attribute", function(){
beforeEach(function() {
this.server = sinon.fakeServer.create();
clearWorkArea();
@ -11,7 +11,7 @@ describe("kt-target attribute", function(){
it('targets an adjacent element properly', function()
{
this.server.respondWith("GET", "/test", "Clicked!");
var btn = make('<button kt-target="#d1" kt-get="/test">Click Me!</button>')
var btn = make('<button hx-target="#d1" hx-get="/test">Click Me!</button>')
var div1 = make('<div id="d1"></div>')
btn.click();
this.server.respond();
@ -21,7 +21,7 @@ describe("kt-target attribute", function(){
it('targets a parent element properly', function()
{
this.server.respondWith("GET", "/test", "Clicked!");
var div1 = make('<div id="d1"><button id="b1" kt-target="#d1" kt-get="/test">Click Me!</button></div>')
var div1 = make('<div id="d1"><button id="b1" hx-target="#d1" hx-get="/test">Click Me!</button></div>')
var btn = byId("b1")
btn.click();
this.server.respond();
@ -31,7 +31,7 @@ describe("kt-target attribute", function(){
it('targets a `this` element properly', function()
{
this.server.respondWith("GET", "/test", "Clicked!");
var div1 = make('<div kt-target="this"><button id="b1" kt-get="/test">Click Me!</button></div>')
var div1 = make('<div hx-target="this"><button id="b1" hx-get="/test">Click Me!</button></div>')
var btn = byId("b1")
btn.click();
this.server.respond();
@ -41,7 +41,7 @@ describe("kt-target attribute", function(){
it('targets a `closest` element properly', function()
{
this.server.respondWith("GET", "/test", "Clicked!");
var div1 = make('<div><p><i><button id="b1" kt-target="closest div" kt-get="/test">Click Me!</button></i></p></div>')
var div1 = make('<div><p><i><button id="b1" hx-target="closest div" hx-get="/test">Click Me!</button></i></p></div>')
var btn = byId("b1")
btn.click();
this.server.respond();
@ -51,7 +51,7 @@ describe("kt-target attribute", function(){
it('targets an inner element properly', function()
{
this.server.respondWith("GET", "/test", "Clicked!");
var btn = make('<button kt-target="#d1" kt-get="/test">Click Me!<div id="d1"></div></button>')
var btn = make('<button hx-target="#d1" hx-get="/test">Click Me!<div id="d1"></div></button>')
var div1 = byId("d1")
btn.click();
this.server.respond();
@ -62,7 +62,7 @@ describe("kt-target attribute", function(){
it('handles bad target gracefully', function()
{
this.server.respondWith("GET", "/test", "Clicked!");
var btn = make('<button kt-target="bad" kt-get="/test">Click Me!</button>')
var btn = make('<button hx-target="bad" hx-get="/test">Click Me!</button>')
btn.click();
this.server.respond();
btn.innerHTML.should.equal("Click Me!");

View File

@ -1,4 +1,4 @@
describe("kt-trigger attribute", function(){
describe("hx-trigger attribute", function(){
beforeEach(function() {
this.server = sinon.fakeServer.create();
clearWorkArea();
@ -12,7 +12,7 @@ describe("kt-trigger attribute", function(){
{
this.server.respondWith("GET", "/test", "Clicked!");
var form = make('<form kt-get="/test" kt-trigger="click">Click Me!</form>');
var form = make('<form hx-get="/test" hx-trigger="click">Click Me!</form>');
form.click();
form.innerHTML.should.equal("Click Me!");
this.server.respond();
@ -26,7 +26,7 @@ describe("kt-trigger attribute", function(){
requests++;
xhr.respond(200, {}, "Requests: " + requests);
});
var input = make('<input kt-trigger="click changed" kt-target="#d1" kt-get="/test" value="foo"/>');
var input = make('<input hx-trigger="click changed" hx-target="#d1" hx-get="/test" value="foo"/>');
var div = make('<div id="d1"></div>');
input.click();
this.server.respond();
@ -50,7 +50,7 @@ describe("kt-trigger attribute", function(){
requests++;
xhr.respond(200, {}, "Requests: " + requests);
});
var input = make('<input kt-trigger="click once" kt-target="#d1" kt-get="/test" value="foo"/>');
var input = make('<input hx-trigger="click once" hx-target="#d1" hx-get="/test" value="foo"/>');
var div = make('<div id="d1"></div>');
input.click();
this.server.respond();
@ -82,7 +82,7 @@ describe("kt-trigger attribute", function(){
});
this.server.autoRespond = true;
this.server.autoRespondAfter = 0;
make('<div kt-trigger="every 10ms" kt-get="/test"/>');
make('<div hx-trigger="every 10ms" hx-get="/test"/>');
});

View File

@ -1,4 +1,4 @@
describe("Core kutty AJAX Tests", function(){
describe("Core htmx AJAX Tests", function(){
beforeEach(function() {
this.server = makeServer();
clearWorkArea();
@ -13,7 +13,7 @@ describe("Core kutty AJAX Tests", function(){
{
this.server.respondWith("GET", "/test", "Clicked!");
var btn = make('<button kt-get="/test">Click Me!</button>')
var btn = make('<button hx-get="/test">Click Me!</button>')
btn.click();
this.server.respond();
btn.innerHTML.should.equal("Clicked!");
@ -21,13 +21,13 @@ describe("Core kutty AJAX Tests", function(){
it('processes inner content properly', function()
{
this.server.respondWith("GET", "/test", '<a kt-get="/test2">Click Me</a>');
this.server.respondWith("GET", "/test", '<a hx-get="/test2">Click Me</a>');
this.server.respondWith("GET", "/test2", "Clicked!");
var div = make('<div kt-get="/test"></div>')
var div = make('<div hx-get="/test"></div>')
div.click();
this.server.respond();
div.innerHTML.should.equal('<a kt-get="/test2">Click Me</a>');
div.innerHTML.should.equal('<a hx-get="/test2">Click Me</a>');
var a = div.querySelector('a');
a.click();
this.server.respond();
@ -36,10 +36,10 @@ describe("Core kutty AJAX Tests", function(){
it('handles swap outerHTML properly', function()
{
this.server.respondWith("GET", "/test", '<a id="a1" kt-get="/test2">Click Me</a>');
this.server.respondWith("GET", "/test", '<a id="a1" hx-get="/test2">Click Me</a>');
this.server.respondWith("GET", "/test2", "Clicked!");
var div = make('<div id="d1" kt-get="/test" kt-swap="outerHTML"></div>')
var div = make('<div id="d1" hx-get="/test" hx-swap="outerHTML"></div>')
div.click();
should.equal(byId("d1"), div);
this.server.respond();
@ -54,11 +54,11 @@ describe("Core kutty AJAX Tests", function(){
var i = 0;
this.server.respondWith("GET", "/test", function(xhr){
i++;
xhr.respond(200, {}, '<a id="a' + i + '" kt-get="/test2" kt-swap="innerHTML">' + i + '</a>');
xhr.respond(200, {}, '<a id="a' + i + '" hx-get="/test2" hx-swap="innerHTML">' + i + '</a>');
});
this.server.respondWith("GET", "/test2", "*");
var div = make('<div kt-get="/test" kt-swap="beforebegin">*</div>')
var div = make('<div hx-get="/test" hx-swap="beforebegin">*</div>')
var parent = div.parentElement;
div.click();
this.server.respond();
@ -84,11 +84,11 @@ describe("Core kutty AJAX Tests", function(){
var i = 0;
this.server.respondWith("GET", "/test", function(xhr){
i++;
xhr.respond(200, {}, '<a id="a' + i + '" kt-get="/test2" kt-swap="innerHTML">' + i + '</a>');
xhr.respond(200, {}, '<a id="a' + i + '" hx-get="/test2" hx-swap="innerHTML">' + i + '</a>');
});
this.server.respondWith("GET", "/test2", "*");
var div = make('<div kt-get="/test" kt-swap="afterbegin">*</div>')
var div = make('<div hx-get="/test" hx-swap="afterbegin">*</div>')
div.click();
this.server.respond();
div.innerText.should.equal("1*");
@ -111,11 +111,11 @@ describe("Core kutty AJAX Tests", function(){
var i = 0;
this.server.respondWith("GET", "/test", function(xhr){
i++;
xhr.respond(200, {}, '<a id="a' + i + '" kt-get="/test2" kt-swap="innerHTML">' + i + '</a>');
xhr.respond(200, {}, '<a id="a' + i + '" hx-get="/test2" hx-swap="innerHTML">' + i + '</a>');
});
this.server.respondWith("GET", "/test2", "*");
var div = make('<div kt-get="/test" kt-swap="afterbegin"></div>')
var div = make('<div hx-get="/test" hx-swap="afterbegin"></div>')
div.click();
this.server.respond();
div.innerText.should.equal("1");
@ -138,11 +138,11 @@ describe("Core kutty AJAX Tests", function(){
var i = 0;
this.server.respondWith("GET", "/test", function(xhr){
i++;
xhr.respond(200, {}, '<a id="a' + i + '" kt-get="/test2" kt-swap="innerHTML">' + i + '</a>');
xhr.respond(200, {}, '<a id="a' + i + '" hx-get="/test2" hx-swap="innerHTML">' + i + '</a>');
});
this.server.respondWith("GET", "/test2", "*");
var div = make('<div kt-get="/test" kt-swap="afterend">*</div>')
var div = make('<div hx-get="/test" hx-swap="afterend">*</div>')
var parent = div.parentElement;
div.click();
this.server.respond();
@ -168,11 +168,11 @@ describe("Core kutty AJAX Tests", function(){
var i = 0;
this.server.respondWith("GET", "/test", function(xhr){
i++;
xhr.respond(200, {}, '<a id="a' + i + '" kt-get="/test2" kt-swap="innerHTML">' + i + '</a>');
xhr.respond(200, {}, '<a id="a' + i + '" hx-get="/test2" hx-swap="innerHTML">' + i + '</a>');
});
this.server.respondWith("GET", "/test2", "*");
var div = make('<div kt-get="/test" kt-swap="beforeend">*</div>')
var div = make('<div hx-get="/test" hx-swap="beforeend">*</div>')
div.click();
this.server.respond();
div.innerText.should.equal("*1");
@ -195,11 +195,11 @@ describe("Core kutty AJAX Tests", function(){
var i = 0;
this.server.respondWith("GET", "/test", function(xhr){
i++;
xhr.respond(200, {}, '<a id="a' + i + '" kt-get="/test2" kt-swap="innerHTML">' + i + '</a>');
xhr.respond(200, {}, '<a id="a' + i + '" hx-get="/test2" hx-swap="innerHTML">' + i + '</a>');
});
this.server.respondWith("GET", "/test2", "*");
var div = make('<div kt-get="/test" kt-swap="beforeend"></div>')
var div = make('<div hx-get="/test" hx-swap="beforeend"></div>')
div.click();
this.server.respond();
div.innerText.should.equal("1");
@ -217,11 +217,11 @@ describe("Core kutty AJAX Tests", function(){
div.innerText.should.equal("**");
});
it('handles kt-target properly', function()
it('handles hx-target properly', function()
{
this.server.respondWith("GET", "/test", "Clicked!");
var btn = make('<button kt-get="/test" kt-target="#s1">Click Me!</button>');
var btn = make('<button hx-get="/test" hx-target="#s1">Click Me!</button>');
var target = make('<span id="s1">Initial</span>');
btn.click();
target.innerHTML.should.equal("Initial");
@ -233,28 +233,28 @@ describe("Core kutty AJAX Tests", function(){
{
this.server.respondWith("GET", "/test", [204, {}, "No Content!"]);
var btn = make('<button kt-get="/test">Click Me!</button>');
var btn = make('<button hx-get="/test">Click Me!</button>');
btn.click();
btn.innerHTML.should.equal("Click Me!");
this.server.respond();
btn.innerHTML.should.equal("Click Me!");
});
it('handles kt-trigger with non-default value', function()
it('handles hx-trigger with non-default value', function()
{
this.server.respondWith("GET", "/test", "Clicked!");
var form = make('<form kt-get="/test" kt-trigger="click">Click Me!</form>');
var form = make('<form hx-get="/test" hx-trigger="click">Click Me!</form>');
form.click();
form.innerHTML.should.equal("Click Me!");
this.server.respond();
form.innerHTML.should.equal("Clicked!");
});
it('handles kt-trigger with load event', function()
it('handles hx-trigger with load event', function()
{
this.server.respondWith("GET", "/test", "Loaded!");
var div = make('<div kt-get="/test" kt-trigger="load">Load Me!</div>');
var div = make('<div hx-get="/test" hx-trigger="load">Load Me!</div>');
div.innerHTML.should.equal("Load Me!");
this.server.respond();
div.innerHTML.should.equal("Loaded!");
@ -266,7 +266,7 @@ describe("Core kutty AJAX Tests", function(){
xhr.overriddenMimeType.should.equal("text/html");
done();
});
var div = make('<div kt-get="/test">Click Me!</div>');
var div = make('<div hx-get="/test">Click Me!</div>');
div.click();
this.server.respond();
});
@ -278,28 +278,28 @@ describe("Core kutty AJAX Tests", function(){
xhr.respond(200, {}, "click " + i);
i++
});
var div = make('<div kt-get="/test"></div>');
var div = make('<div hx-get="/test"></div>');
div.click();
div.click();
this.server.respond();
div.innerHTML.should.equal("click 1");
});
it('properly handles kt-select for basic situation', function()
it('properly handles hx-select for basic situation', function()
{
var i = 1;
this.server.respondWith("GET", "/test", "<div id='d1'>foo</div><div id='d2'>bar</div>");
var div = make('<div kt-get="/test" kt-select="#d1"></div>');
var div = make('<div hx-get="/test" hx-select="#d1"></div>');
div.click();
this.server.respond();
div.innerHTML.should.equal("<div id=\"d1\">foo</div>");
});
it('properly handles kt-select for full html document situation', function()
it('properly handles hx-select for full html document situation', function()
{
var i = 1;
this.server.respondWith("GET", "/test", "<html><body><div id='d1'>foo</div><div id='d2'>bar</div></body></html>");
var div = make('<div kt-get="/test" kt-select="#d1"></div>');
var div = make('<div hx-get="/test" hx-select="#d1"></div>');
div.click();
this.server.respond();
div.innerHTML.should.equal("<div id=\"d1\">foo</div>");
@ -313,7 +313,7 @@ describe("Core kutty AJAX Tests", function(){
xhr.respond(204, {}, "");
});
var form = make('<form kt-post="/test" kt-trigger="click">' +
var form = make('<form hx-post="/test" hx-trigger="click">' +
'<input id="cb1" name="c1" value="cb1" type="checkbox">'+
'<input id="cb2" name="c1" value="cb2" type="checkbox">'+
'<input id="cb3" name="c1" value="cb3" type="checkbox">'+

View File

@ -1,4 +1,4 @@
describe("Core kutty API test", function(){
describe("Core htmx API test", function(){
beforeEach(function() {
this.server = makeServer();
clearWorkArea();
@ -9,22 +9,22 @@ describe("Core kutty API test", function(){
});
it('version is correct', function(){
kutty.version.should.equal("0.0.2");
htmx.version.should.equal("0.0.2");
});
it('onLoad is called... onLoad', function(){
// also tests on/off
this.server.respondWith("GET", "/test", "<div id='d1' kt-get='/test'></div>")
var helper = kutty.onLoad(function (elt) {
this.server.respondWith("GET", "/test", "<div id='d1' hx-get='/test'></div>")
var helper = htmx.onLoad(function (elt) {
elt.setAttribute("foo", "bar");
});
try {
var div = make("<div id='d1' kt-get='/test' kt-swap='outerHTML'></div>");
var div = make("<div id='d1' hx-get='/test' hx-swap='outerHTML'></div>");
div.click();
this.server.respond();
byId("d1").getAttribute("foo").should.equal("bar");
} finally {
kutty.off("load.kutty", helper);
htmx.off("load.htmx", helper);
}
});
@ -32,11 +32,11 @@ describe("Core kutty API test", function(){
var div = make("<div/>");
var myEventCalled = false;
var detailStr = "";
kutty.on("myEvent", function(evt){
htmx.on("myEvent", function(evt){
myEventCalled = true;
detailStr = evt.detail.str;
})
kutty.trigger(div, "myEvent", {str:"foo"})
htmx.trigger(div, "myEvent", {str:"foo"})
myEventCalled.should.equal(true);
detailStr.should.equal("foo");
@ -44,55 +44,55 @@ describe("Core kutty API test", function(){
it('should find properly', function(){
var div = make("<div id='d1' class='c1 c2'>");
div.should.equal(kutty.find("#d1"));
div.should.equal(kutty.find(".c1"));
div.should.equal(kutty.find(".c2"));
div.should.equal(kutty.find(".c1.c2"));
div.should.equal(htmx.find("#d1"));
div.should.equal(htmx.find(".c1"));
div.should.equal(htmx.find(".c2"));
div.should.equal(htmx.find(".c1.c2"));
});
it('should find properly from elt', function(){
var div = make("<div><a id='a1'></a><a id='a2'></a></div>");
kutty.find(div, "a").id.should.equal('a1');
htmx.find(div, "a").id.should.equal('a1');
});
it('should find all properly', function(){
var div = make("<div class='c1 c2 c3'><div class='c1 c2'><div class='c1'>");
kutty.findAll(".c1").length.should.equal(3);
kutty.findAll(".c2").length.should.equal(2);
kutty.findAll(".c3").length.should.equal(1);
htmx.findAll(".c1").length.should.equal(3);
htmx.findAll(".c2").length.should.equal(2);
htmx.findAll(".c3").length.should.equal(1);
});
it('should find all properly from elt', function(){
var div = make("<div><div class='c1 c2 c3'><div class='c1 c2'><div class='c1'></div>");
kutty.findAll(div, ".c1").length.should.equal(3);
kutty.findAll(div, ".c2").length.should.equal(2);
kutty.findAll(div,".c3").length.should.equal(1);
htmx.findAll(div, ".c1").length.should.equal(3);
htmx.findAll(div, ".c2").length.should.equal(2);
htmx.findAll(div,".c3").length.should.equal(1);
});
it('should find closest element properly', function () {
var div = make("<div><a id='a1'></a><a id='a2'></a></div>");
var a = kutty.find(div, "a");
kutty.closest(a, "div").should.equal(div);
var a = htmx.find(div, "a");
htmx.closest(a, "div").should.equal(div);
});
it('should remove element properly', function () {
var div = make("<div><a></a></div>");
var a = kutty.find(div, "a");
kutty.remove(a);
var a = htmx.find(div, "a");
htmx.remove(a);
div.innerHTML.should.equal("");
});
it('should add class properly', function () {
var div = make("<div></div>");
div.classList.contains("foo").should.equal(false);
kutty.addClass(div, "foo");
htmx.addClass(div, "foo");
div.classList.contains("foo").should.equal(true);
});
it('should add class properly after delay', function (done) {
var div = make("<div></div>");
div.classList.contains("foo").should.equal(false);
kutty.addClass(div, "foo", 10);
htmx.addClass(div, "foo", 10);
div.classList.contains("foo").should.equal(false);
setTimeout(function () {
div.classList.contains("foo").should.equal(true);
@ -102,17 +102,17 @@ describe("Core kutty API test", function(){
it('should remove class properly', function () {
var div = make("<div></div>");
kutty.addClass(div, "foo");
htmx.addClass(div, "foo");
div.classList.contains("foo").should.equal(true);
kutty.removeClass(div, "foo");
htmx.removeClass(div, "foo");
div.classList.contains("foo").should.equal(false);
});
it('should add class properly after delay', function (done) {
var div = make("<div></div>");
kutty.addClass(div, "foo");
htmx.addClass(div, "foo");
div.classList.contains("foo").should.equal(true);
kutty.removeClass(div, "foo", 10);
htmx.removeClass(div, "foo", 10);
div.classList.contains("foo").should.equal(true);
setTimeout(function () {
div.classList.contains("foo").should.equal(false);
@ -123,9 +123,9 @@ describe("Core kutty API test", function(){
it('should toggle class properly', function () {
var div = make("<div></div>");
div.classList.contains("foo").should.equal(false);
kutty.toggleClass(div, "foo");
htmx.toggleClass(div, "foo");
div.classList.contains("foo").should.equal(true);
kutty.toggleClass(div, "foo");
htmx.toggleClass(div, "foo");
div.classList.contains("foo").should.equal(false);
});
@ -138,19 +138,19 @@ describe("Core kutty API test", function(){
div2.classList.contains("foo").should.equal(false);
div3.classList.contains("foo").should.equal(false);
kutty.takeClass(div1, "foo");
htmx.takeClass(div1, "foo");
div1.classList.contains("foo").should.equal(true);
div2.classList.contains("foo").should.equal(false);
div3.classList.contains("foo").should.equal(false);
kutty.takeClass(div2, "foo");
htmx.takeClass(div2, "foo");
div1.classList.contains("foo").should.equal(false);
div2.classList.contains("foo").should.equal(true);
div3.classList.contains("foo").should.equal(false);
kutty.takeClass(div3, "foo");
htmx.takeClass(div3, "foo");
div1.classList.contains("foo").should.equal(false);
div2.classList.contains("foo").should.equal(false);

View File

@ -1,4 +1,4 @@
describe("Core kutty Events", function() {
describe("Core htmx Events", function() {
beforeEach(function () {
this.server = makeServer();
clearWorkArea();
@ -8,24 +8,24 @@ describe("Core kutty Events", function() {
clearWorkArea();
});
it("load.kutty fires properly", function () {
var handler = kutty.on("load.kutty", function (evt) {
it("load.htmx fires properly", function () {
var handler = htmx.on("load.htmx", function (evt) {
called = true;
});
try {
this.server.respondWith("GET", "/test", "<div></div>");
var called = false;
var div = make("<div kt-get='/test'></div>");
var div = make("<div hx-get='/test'></div>");
div.click();
this.server.respond();
should.equal(called, true);
} finally {
kutty.off("load.kutty", handler);
htmx.off("load.htmx", handler);
}
});
it("configRequest.kutty allows attribute addition", function () {
var handler = kutty.on("configRequest.kutty", function (evt) {
it("configRequest.htmx allows attribute addition", function () {
var handler = htmx.on("configRequest.htmx", function (evt) {
evt.detail.parameters['param'] = "true";
});
try {
@ -33,48 +33,48 @@ describe("Core kutty Events", function() {
this.server.respondWith("POST", "/test", function (xhr) {
param = parseParams(xhr.requestBody)['param'];
});
var div = make("<div kt-post='/test'></div>");
var div = make("<div hx-post='/test'></div>");
div.click();
this.server.respond();
param.should.equal("true");
} finally {
kutty.off("configRequest.kutty", handler);
htmx.off("configRequest.htmx", handler);
}
});
it("configRequest.kutty allows attribute removal", function () {
it("configRequest.htmx allows attribute removal", function () {
var param = "foo";
var handler = kutty.on("configRequest.kutty", function (evt) {
var handler = htmx.on("configRequest.htmx", function (evt) {
delete evt.detail.parameters['param'];
});
try {
this.server.respondWith("POST", "/test", function (xhr) {
param = parseParams(xhr.requestBody)['param'];
});
var div = make("<form kt-trigger='click' kt-post='/test'><input name='param' value='foo'></form>");
var div = make("<form hx-trigger='click' hx-post='/test'><input name='param' value='foo'></form>");
div.click();
this.server.respond();
should.equal(param, undefined);
} finally {
kutty.off("configRequest.kutty", handler);
htmx.off("configRequest.htmx", handler);
}
});
it("configRequest.kutty allows header tweaking", function () {
it("configRequest.htmx allows header tweaking", function () {
var header = "foo";
var handler = kutty.on("configRequest.kutty", function (evt) {
var handler = htmx.on("configRequest.htmx", function (evt) {
evt.detail.headers['X-My-Header'] = "bar";
});
try {
this.server.respondWith("POST", "/test", function (xhr) {
header = xhr.requestHeaders['X-My-Header'];
});
var div = make("<form kt-trigger='click' kt-post='/test'><input name='param' value='foo'></form>");
var div = make("<form hx-trigger='click' hx-post='/test'><input name='param' value='foo'></form>");
div.click();
this.server.respond();
should.equal(header, "bar");
} finally {
kutty.off("configRequest.kutty", handler);
htmx.off("configRequest.htmx", handler);
}
});

View File

@ -1,4 +1,4 @@
describe("Core kutty AJAX headers", function() {
describe("Core htmx AJAX headers", function() {
beforeEach(function () {
this.server = makeServer();
clearWorkArea();
@ -13,7 +13,7 @@ describe("Core kutty AJAX headers", function() {
xhr.requestHeaders['X-KT-Request'].should.be.equal('true');
xhr.respond(200, {}, "");
});
var div = make('<div kt-get="/test"></div>');
var div = make('<div hx-get="/test"></div>');
div.click();
this.server.respond();
})
@ -23,7 +23,7 @@ describe("Core kutty AJAX headers", function() {
xhr.requestHeaders['X-KT-Trigger'].should.equal('d1');
xhr.respond(200, {}, "");
});
var div = make('<div id="d1" kt-get="/test"></div>');
var div = make('<div id="d1" hx-get="/test"></div>');
div.click();
this.server.respond();
})
@ -33,7 +33,7 @@ describe("Core kutty AJAX headers", function() {
xhr.requestHeaders['X-KT-Trigger-Name'].should.equal('n1');
xhr.respond(200, {}, "");
});
var div = make('<button name="n1" kt-get="/test"></button>');
var div = make('<button name="n1" hx-get="/test"></button>');
div.click();
this.server.respond();
})
@ -43,7 +43,7 @@ describe("Core kutty AJAX headers", function() {
xhr.requestHeaders['X-KT-Target'].should.equal('d1');
xhr.respond(200, {}, "");
});
var div = make('<div kt-target="#d1" kt-get="/test"></div><div id="d1" ></div>');
var div = make('<div hx-target="#d1" hx-get="/test"></div><div id="d1" ></div>');
div.click();
this.server.respond();
})
@ -51,7 +51,7 @@ describe("Core kutty AJAX headers", function() {
it("should handle simple string X-KT-Trigger response header properly", function(){
this.server.respondWith("GET", "/test", [200, {"X-KT-Trigger" : "foo"}, ""]);
var div = make('<div kt-get="/test"></div>');
var div = make('<div hx-get="/test"></div>');
var invokedEvent = false;
div.addEventListener("foo", function (evt) {
invokedEvent = true;
@ -64,7 +64,7 @@ describe("Core kutty AJAX headers", function() {
it("should handle basic JSON X-KT-Trigger response header properly", function(){
this.server.respondWith("GET", "/test", [200, {"X-KT-Trigger" : "{\"foo\":null}"}, ""]);
var div = make('<div kt-get="/test"></div>');
var div = make('<div hx-get="/test"></div>');
var invokedEvent = false;
div.addEventListener("foo", function (evt) {
invokedEvent = true;
@ -79,7 +79,7 @@ describe("Core kutty AJAX headers", function() {
it("should handle JSON with array arg X-KT-Trigger response header properly", function(){
this.server.respondWith("GET", "/test", [200, {"X-KT-Trigger" : "{\"foo\":[1, 2, 3]}"}, ""]);
var div = make('<div kt-get="/test"></div>');
var div = make('<div hx-get="/test"></div>');
var invokedEvent = false;
div.addEventListener("foo", function (evt) {
invokedEvent = true;
@ -94,7 +94,7 @@ describe("Core kutty AJAX headers", function() {
it("should handle JSON with array arg X-KT-Trigger response header properly", function(){
this.server.respondWith("GET", "/test", [200, {"X-KT-Trigger" : "{\"foo\":{\"a\":1, \"b\":2}}"}, ""]);
var div = make('<div kt-get="/test"></div>');
var div = make('<div hx-get="/test"></div>');
var invokedEvent = false;
div.addEventListener("foo", function (evt) {
invokedEvent = true;

View File

@ -1,4 +1,4 @@
describe("Core kutty internals Tests", function() {
describe("Core htmx internals Tests", function() {
beforeEach(function () {
this.server = makeServer();
clearWorkArea();
@ -9,15 +9,15 @@ describe("Core kutty internals Tests", function() {
});
it("makeFragment works with janky stuff", function(){
kutty._("makeFragment")("<html></html>").tagName.should.equal("BODY");
kutty._("makeFragment")("<html><body></body></html>").tagName.should.equal("BODY");
htmx._("makeFragment")("<html></html>").tagName.should.equal("BODY");
htmx._("makeFragment")("<html><body></body></html>").tagName.should.equal("BODY");
//NB - the tag name should be the *parent* element hosting the HTML since we use the fragment children
// for the swap
kutty._("makeFragment")("<td></td>").tagName.should.equal("TR");
kutty._("makeFragment")("<thead></thead>").tagName.should.equal("TABLE");
kutty._("makeFragment")("<col></col>").tagName.should.equal("COLGROUP");
kutty._("makeFragment")("<tr></tr>").tagName.should.equal("TBODY");
htmx._("makeFragment")("<td></td>").tagName.should.equal("TR");
htmx._("makeFragment")("<thead></thead>").tagName.should.equal("TABLE");
htmx._("makeFragment")("<col></col>").tagName.should.equal("COLGROUP");
htmx._("makeFragment")("<tr></tr>").tagName.should.equal("TBODY");
})
});

View File

@ -1,4 +1,4 @@
describe("Core kutty Parameter Handling", function() {
describe("Core htmx Parameter Handling", function() {
beforeEach(function () {
this.server = makeServer();
clearWorkArea();
@ -10,20 +10,20 @@ describe("Core kutty Parameter Handling", function() {
it('Input includes value', function () {
var input = make('<input name="foo" value="bar"/>');
var vals = kutty._('getInputValues')(input);
var vals = htmx._('getInputValues')(input);
vals['foo'].should.equal('bar');
})
it('Input includes value on get', function () {
var input = make('<input name="foo" value="bar"/>');
var vals = kutty._('getInputValues')(input, "get");
var vals = htmx._('getInputValues')(input, "get");
vals['foo'].should.equal('bar');
})
it('Input includes form', function () {
var form = make('<form><input id="i1" name="foo" value="bar"/><input id="i2" name="do" value="rey"/></form>');
var input = byId('i1');
var vals = kutty._('getInputValues')(input);
var vals = htmx._('getInputValues')(input);
vals['foo'].should.equal('bar');
vals['do'].should.equal('rey');
})
@ -31,7 +31,7 @@ describe("Core kutty Parameter Handling", function() {
it('Input doesnt include form on get', function () {
var form = make('<form><input id="i1" name="foo" value="bar"/><input id="i2" name="do" value="rey"/></form>');
var input = byId('i1');
var vals = kutty._('getInputValues')(input, 'get');
var vals = htmx._('getInputValues')(input, 'get');
vals['foo'].should.equal('bar');
should.equal(vals['do'], undefined);
})
@ -39,75 +39,75 @@ describe("Core kutty Parameter Handling", function() {
it('non-input includes form', function () {
var form = make('<form><div id="d1"/><input id="i2" name="do" value="rey"/></form>');
var div = byId('d1');
var vals = kutty._('getInputValues')(div, "post");
var vals = htmx._('getInputValues')(div, "post");
vals['do'].should.equal('rey');
})
it('non-input doesnt include form on get', function () {
var form = make('<form><div id="d1"/><input id="i2" name="do" value="rey"/></form>');
var div = byId('d1');
var vals = kutty._('getInputValues')(div, "get");
var vals = htmx._('getInputValues')(div, "get");
should.equal(vals['do'], undefined);
})
it('Basic form works on get', function () {
var form = make('<form><input id="i1" name="foo" value="bar"/><input id="i2" name="do" value="rey"/></form>');
var vals = kutty._('getInputValues')(form, 'get');
var vals = htmx._('getInputValues')(form, 'get');
vals['foo'].should.equal('bar');
vals['do'].should.equal('rey');
})
it('Basic form works on non-get', function () {
var form = make('<form><input id="i1" name="foo" value="bar"/><input id="i2" name="do" value="rey"/></form>');
var vals = kutty._('getInputValues')(form, 'post');
var vals = htmx._('getInputValues')(form, 'post');
vals['foo'].should.equal('bar');
vals['do'].should.equal('rey');
})
it('Double values are included as array', function () {
var form = make('<form><input id="i1" name="foo" value="bar"/><input id="i2" name="do" value="rey"/><input id="i2" name="do" value="rey"/></form>');
var vals = kutty._('getInputValues')(form);
var vals = htmx._('getInputValues')(form);
vals['foo'].should.equal('bar');
vals['do'].should.deep.equal(['rey', 'rey']);
})
it('kt-include works with form', function () {
it('hx-include works with form', function () {
var form = make('<form id="f1"><input id="i1" name="foo" value="bar"/><input id="i2" name="do" value="rey"/><input id="i2" name="do" value="rey"/></form>');
var div = make('<div kt-include="#f1"></div>');
var vals = kutty._('getInputValues')(div);
var div = make('<div hx-include="#f1"></div>');
var vals = htmx._('getInputValues')(div);
vals['foo'].should.equal('bar');
vals['do'].should.deep.equal(['rey', 'rey']);
})
it('kt-include works with input', function () {
it('hx-include works with input', function () {
var form = make('<form id="f1"><input id="i1" name="foo" value="bar"/><input id="i2" name="do" value="rey"/><input id="i2" name="do" value="rey"/></form>');
var div = make('<div kt-include="#i1"></div>');
var vals = kutty._('getInputValues')(div);
var div = make('<div hx-include="#i1"></div>');
var vals = htmx._('getInputValues')(div);
vals['foo'].should.equal('bar');
should.equal(vals['do'], undefined);
})
it('kt-include works with two inputs', function () {
it('hx-include works with two inputs', function () {
var form = make('<form id="f1"><input id="i1" name="foo" value="bar"/><input id="i2" name="do" value="rey"/><input id="i2" name="do" value="rey"/></form>');
var div = make('<div kt-include="#i1, #i2"></div>');
var vals = kutty._('getInputValues')(div);
var div = make('<div hx-include="#i1, #i2"></div>');
var vals = htmx._('getInputValues')(div);
vals['foo'].should.equal('bar');
vals['do'].should.deep.equal(['rey', 'rey']);
})
it('kt-include works with two inputs, plus form', function () {
it('hx-include works with two inputs, plus form', function () {
var form = make('<form id="f1"><input id="i1" name="foo" value="bar"/><input id="i2" name="do" value="rey"/><input id="i2" name="do" value="rey"/></form>');
var div = make('<div kt-include="#i1, #i2, #f1"></div>');
var vals = kutty._('getInputValues')(div);
var div = make('<div hx-include="#i1, #i2, #f1"></div>');
var vals = htmx._('getInputValues')(div);
vals['foo'].should.equal('bar');
vals['do'].should.deep.equal(['rey', 'rey']);
})
it('correctly URL escapes values', function () {
kutty._("urlEncode")({}).should.equal("");
kutty._("urlEncode")({"foo": "bar"}).should.equal("foo=bar");
kutty._("urlEncode")({"foo": "bar", "do" : "rey"}).should.equal("foo=bar&do=rey");
kutty._("urlEncode")({"foo": "bar", "do" : ["rey", "blah"]}).should.equal("foo=bar&do=rey&do=blah");
htmx._("urlEncode")({}).should.equal("");
htmx._("urlEncode")({"foo": "bar"}).should.equal("foo=bar");
htmx._("urlEncode")({"foo": "bar", "do" : "rey"}).should.equal("foo=bar&do=rey");
htmx._("urlEncode")({"foo": "bar", "do" : ["rey", "blah"]}).should.equal("foo=bar&do=rey&do=blah");
});
});

View File

@ -1,4 +1,4 @@
describe("Core kutty Regression Tests", function(){
describe("Core htmx Regression Tests", function(){
beforeEach(function() {
this.server = makeServer();
@ -18,21 +18,21 @@ describe("Core kutty Regression Tests", function(){
'</svg>')
});
it ('Handles https://github.com/bigskysoftware/kutty/issues/4 properly', function() {
it ('Handles https://github.com/bigskysoftware/htmx/issues/4 properly', function() {
this.server.respondWith("GET", "/index2a.php",
"<div id='message' kt-swap-oob='true'>I came from message oob swap I should be second</div>" +
"<div id='message2' kt-swap-oob='true'>I came from a message2 oob swap I should be third but I am in the wrong spot</div>" +
"<div id='message' hx-swap-oob='true'>I came from message oob swap I should be second</div>" +
"<div id='message2' hx-swap-oob='true'>I came from a message2 oob swap I should be third but I am in the wrong spot</div>" +
"I'm page2 content (non-swap) I should be first")
var h1 = make("<h1 kt-get='/index2a.php' kt-target='#page2' kt-trigger='click'>Kutty CLICK ME</h1>" +
var h1 = make("<h1 hx-get='/index2a.php' hx-target='#page2' hx-trigger='click'>Kutty CLICK ME</h1>" +
"<div id='page2' ></div>" +
"<div id='message'></div>" +
"<div id='message2'></div>")
h1.click();
this.server.respond();
kutty.find("#page2").innerHTML.should.equal("I'm page2 content (non-swap) I should be first")
kutty.find("#message").innerHTML.should.equal("I came from message oob swap I should be second")
kutty.find("#message2").innerHTML.should.equal("I came from a message2 oob swap I should be third but I am in the wrong spot")
htmx.find("#page2").innerHTML.should.equal("I'm page2 content (non-swap) I should be first")
htmx.find("#message").innerHTML.should.equal("I came from message oob swap I should be second")
htmx.find("#message2").innerHTML.should.equal("I came from a message2 oob swap I should be third but I am in the wrong spot")
});
})

View File

@ -1,4 +1,4 @@
describe("Core kutty AJAX Verbs", function() {
describe("Core htmx AJAX Verbs", function() {
beforeEach(function () {
this.server = makeServer();
clearWorkArea();
@ -10,7 +10,7 @@ describe("Core kutty AJAX Verbs", function() {
it('handles basic posts properly', function () {
this.server.respondWith("POST", "/test", "post");
var div = make('<div kt-post="/test">click me</div>');
var div = make('<div hx-post="/test">click me</div>');
div.click();
this.server.respond();
div.innerHTML.should.equal("post");
@ -18,7 +18,7 @@ describe("Core kutty AJAX Verbs", function() {
it('handles basic put properly', function () {
this.server.respondWith("PUT", "/test", "put");
var div = make('<div kt-put="/test">click me</div>');
var div = make('<div hx-put="/test">click me</div>');
div.click();
this.server.respond();
div.innerHTML.should.equal("put");
@ -26,7 +26,7 @@ describe("Core kutty AJAX Verbs", function() {
it('handles basic patch properly', function () {
this.server.respondWith("PATCH", "/test", "patch");
var div = make('<div kt-patch="/test">click me</div>');
var div = make('<div hx-patch="/test">click me</div>');
div.click();
this.server.respond();
div.innerHTML.should.equal("patch");
@ -34,7 +34,7 @@ describe("Core kutty AJAX Verbs", function() {
it('handles basic delete properly', function () {
this.server.respondWith("DELETE", "/test", "delete");
var div = make('<div kt-delete="/test">click me</div>');
var div = make('<div hx-delete="/test">click me</div>');
div.click();
this.server.respond();
div.innerHTML.should.equal("delete");

View File

@ -9,11 +9,11 @@
<meta http-equiv="expires" content="0" />
<meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" />
<meta http-equiv="pragma" content="no-cache" />
<meta name="kutty-config" content='{"historyEnabled":false}'>
<meta name="htmx-config" content='{"historyEnabled":false}'>
</head>
<body style="padding:20px;font-family: sans-serif">
<h1>kutty.js test suite</h1>
<h1>htmx.js test suite</h1>
<h2>Scratch Page</h2>
<ul>
@ -43,7 +43,7 @@
<script src="../node_modules/chai/chai.js"></script>
<script src="../node_modules/mocha/mocha.js"></script>
<script src="../node_modules/sinon/pkg/sinon.js"></script>
<script src="../src/kutty.js"></script>
<script src="../src/htmx.js"></script>
<script class="mocha-init">
mocha.setup('bdd');
mocha.checkLeaks();
@ -62,22 +62,22 @@
<script src="core/regressions.js"></script>
<!-- attribute tests -->
<script src="attributes/kt-boost.js"></script>
<script src="attributes/kt-classes.js"></script>
<script src="attributes/kt-delete.js"></script>
<script src="attributes/kt-error-url.js"></script>
<script src="attributes/kt-get.js"></script>
<script src="attributes/kt-include.js"></script>
<script src="attributes/kt-indicator.js"></script>
<script src="attributes/kt-params.js"></script>
<script src="attributes/kt-patch.js"></script>
<script src="attributes/kt-post.js"></script>
<script src="attributes/kt-push-url.js"></script>
<script src="attributes/kt-put.js"></script>
<script src="attributes/kt-swap-oob.js"></script>
<script src="attributes/kt-swap.js"></script>
<script src="attributes/kt-target.js"></script>
<script src="attributes/kt-trigger.js"></script>
<script src="attributes/hx-boost.js"></script>
<script src="attributes/hx-classes.js"></script>
<script src="attributes/hx-delete.js"></script>
<script src="attributes/hx-error-url.js"></script>
<script src="attributes/hx-get.js"></script>
<script src="attributes/hx-include.js"></script>
<script src="attributes/hx-indicator.js"></script>
<script src="attributes/hx-params.js"></script>
<script src="attributes/hx-patch.js"></script>
<script src="attributes/hx-post.js"></script>
<script src="attributes/hx-push-url.js"></script>
<script src="attributes/hx-put.js"></script>
<script src="attributes/hx-swap-oob.js"></script>
<script src="attributes/hx-swap.js"></script>
<script src="attributes/hx-target.js"></script>
<script src="attributes/hx-trigger.js"></script>
<!-- events last so they don't screw up other tests -->
<script src="core/events.js"></script>
@ -90,7 +90,7 @@
</script>
<em>Work Area</em>
<hr/>
<div id="work-area" kt-history-elt>
<div id="work-area" hx-history-elt>
</div>
</body>
</html>

View File

@ -10,7 +10,7 @@
<script src="../../node_modules/chai/chai.js"></script>
<script src="../../node_modules/mocha/mocha.js"></script>
<script src="../../node_modules/sinon/pkg/sinon.js"></script>
<script src="../../src/kutty.js"></script>
<script src="../../src/htmx.js"></script>
<script class="mocha-init">
mocha.setup('bdd');
mocha.checkLeaks();
@ -34,7 +34,7 @@
this.server.respondWith("GET", "/test", "second");
getWorkArea().innerHTML.should.be.equal("");
var div = make('<div kt-push-url="true" kt-get="/test">first</div>');
var div = make('<div hx-push-url="true" hx-get="/test">first</div>');
div.click();
this.server.respond();
getWorkArea().textContent.should.equal("second")
@ -53,7 +53,7 @@
});
getWorkArea().innerHTML.should.equal("");
var div = make('<div kt-push-url="true" kt-get="/test" class="">0</div>');
var div = make('<div hx-push-url="true" hx-get="/test" class="">0</div>');
div.click();
this.server.respond();
getWorkArea().textContent.should.equal("1")
@ -77,7 +77,7 @@
this.server.respondWith("GET", "/test", "second");
getWorkArea().innerHTML.should.equal("");
var div = make('<div kt-push-url="true" kt-get="/test" class="">first</div>');
var div = make('<div hx-push-url="true" hx-get="/test" class="">first</div>');
div.click();
this.server.respond();
getWorkArea().textContent.should.equal("second")
@ -104,7 +104,7 @@
</script>
<em>Work Area</em>
<hr/>
<div id="work-area" kt-history-elt>
<div id="work-area" hx-history-elt>
</div>
</body>
</html>

View File

@ -2,11 +2,11 @@
<head>
<meta charset="utf-8" />
<title>Test if indicators are invisible by default</title>
<script src="../../src/kutty.js"></script>
<script src="../../src/htmx.js"></script>
</head>
<body style="padding:20px;font-family: sans-serif">
<script src="../../node_modules/sinon/pkg/sinon.js"></script>
<script src="../../src/kutty.js"></script>
<script src="../../src/htmx.js"></script>
<script src="../util/util.js"></script>
<script>
server = makeServer();
@ -19,10 +19,10 @@
})
</script>
<h1>Prompt & Confirm Tests</h1>
<button kt-get="/prompt" kt-prompt="Enter some text and it should be echoed in this button">Click For Prompt</button>
<button hx-get="/prompt" hx-prompt="Enter some text and it should be echoed in this button">Click For Prompt</button>
<br/>
<br/>
<br/>
<button kt-get="/confirm" kt-confirm="Confirm The Action">Click For Confirm</button>
<button hx-get="/confirm" hx-confirm="Confirm The Action">Click For Confirm</button>
</body>
</html>

View File

@ -1,9 +1,9 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="kutty-config" content='{"includeIndicatorStyles":false}'>
<meta name="htmx-config" content='{"includeIndicatorStyles":false}'>
<title>Test if the includeIndicatorStyles meta option works</title>
<script src="../../src/kutty.js"></script>
<script src="../../src/htmx.js"></script>
</head>
<body style="padding:20px;font-family: sans-serif">
<h1>You should see bars here:</h1>
@ -11,6 +11,6 @@
We are overriding the normal CSS inclusion with the meta directive <code>{"includeIndicatorStyles":false}</code>
so you should see the indicator because it is not being hidden by the default classes.
</p>
<img class="kutty-indicator" src="../img/bars.svg" width="200">
<img class="htmx-indicator" src="../img/bars.svg" width="200">
</body>
</html>

View File

@ -2,10 +2,10 @@
<head>
<meta charset="utf-8" />
<title>Test if indicators are invisible by default</title>
<script src="../../src/kutty.js"></script>
<script src="../../src/htmx.js"></script>
</head>
<body style="padding:20px;font-family: sans-serif">
<h1>You should not see bars here:</h1>
<img class="kutty-indicator" src="../img/bars.svg" width="200">
<img class="htmx-indicator" src="../img/bars.svg" width="200">
</body>
</html>

View File

@ -9,7 +9,7 @@
opacity: 0;
}
.kt-show-indicator .indicator {
.hx-show-indicator .indicator {
opacity: 100%;
}
@ -19,18 +19,18 @@
</head>
<body style="padding:20px;font-family: sans-serif">
<script src="../node_modules/sinon/pkg/sinon.js"></script>
<script src="../src/kutty.js"></script>
<script src="../src/htmx.js"></script>
<script src="util/util.js"></script>
<script src="util/scratch_server.js"></script>
<script>
// this.server.respondWith("GET", "/test", '<a kt-get="/test2">Click Me</a>');
// this.server.respondWith("GET", "/test", '<a hx-get="/test2">Click Me</a>');
// this.server.respondWith("GET", "/test2", "Clicked!");
//
// make('<div kt-get="/test">dd</div>')
// make('<div hx-get="/test">dd</div>')
this.server.respondWith("GET", "/test", '<div id="d1" style="color: red; margin: 100px">Foo</div>');
make('<div kt-swap="outerHTML" kt-get="/test" kt-push-url="true" id="d1">Foo</div>');
make('<div hx-swap="outerHTML" hx-get="/test" hx-push-url="true" id="d1">Foo</div>');
</script>
@ -43,7 +43,7 @@ Autorespond: <input id="autorespond" type="checkbox" onclick="toggleAutoRespond(
<em>Work Area</em>
<hr/>
<div id="work-area" kt-history-elt>
<div id="work-area" hx-history-elt>
</div>

View File

@ -1,5 +1,5 @@
var server = makeServer();
var autoRespond = localStorage.getItem('kt-scratch-autorespond') == "true";
var autoRespond = localStorage.getItem('hx-scratch-autorespond') == "true";
server.autoRespond = autoRespond;
ready(function () {
if (autoRespond) {
@ -8,10 +8,10 @@ ready(function () {
})
function toggleAutoRespond() {
if (server.autoRespond) {
localStorage.removeItem('kt-scratch-autorespond');
localStorage.removeItem('hx-scratch-autorespond');
server.autoRespond = false;
} else {
localStorage.setItem('kt-scratch-autorespond', 'true');
localStorage.setItem('hx-scratch-autorespond', 'true');
server.autoRespond = true;
}
}

View File

@ -1,6 +1,6 @@
/* Test Utilities */
kutty.logAll();
htmx.logAll();
function byId(id) {
return document.getElementById(id);
@ -13,7 +13,7 @@ function make(htmlStr) {
var wa = getWorkArea();
for (var i = fragment.childNodes.length - 1; i >= 0; i--) {
var child = fragment.childNodes[i];
kutty.process(child);
htmx.process(child);
wa.appendChild(child);
}
return wa.lastChild;

View File

@ -1,19 +1,19 @@
<html lang="en">
<head>
<title>&lt;/> kutty - high power tools for html</title>
<title>&lt;/> htmx - high power tools for html</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/css/site.css"/>
<link rel="stylesheet" href="/css/prism-kutty.css"/>
<link rel="stylesheet" href="/css/prism-htmx.css"/>
<script src="https://unpkg.com/prismjs@1.20.0/components/prism-core.min.js"></script>
<script src="https://unpkg.com/prismjs@1.20.0/plugins/autoloader/prism-autoloader.min.js"></script>
<script src="/js/kutty.js"></script>
<script src="/js/htmx.js"></script>
<script>
kutty.logger = function(elt, event, data) {
htmx.logger = function(elt, event, data) {
if(console) {
//console.log(event, elt, data);
}
}
kutty.onLoad(function(){
htmx.onLoad(function(){
Prism.highlightAll();
})
</script>
@ -24,9 +24,9 @@
<div class="row">
<div class="2 col">
{% if page.url.indexOf("/examples/") == 0 %}
<span onclick="document.location = '/';" class="logo light">&lt;<a>/</a>&gt; k<a>u</a>tty</span>
<span onclick="document.location = '/';" class="logo light">&lt;<a>/</a>&gt; htm<a>x</a></span>
{% else %}
<span kt-get="/" kt-target="body" kt-push-url="true" class="logo light">&lt;<a>/</a>&gt; k<a>u</a>tty</span>
<span hx-get="/" hx-target="body" hx-push-url="true" class="logo light">&lt;<a>/</a>&gt; htm<a>x</a></span>
{% endif %}
<svg onclick="document.getElementById('nav').classList.toggle('show')" class="hamburger" viewBox="0 0 100 80" width="25" height="25" style="margin-bottom:-5px">
<rect width="100" height="20" style="fill:rgb(52, 101, 164)" rx="10"></rect>
@ -37,7 +37,7 @@
{% if page.url.indexOf("/examples/") == 0 %}
<div id="nav" class="10 col nav"> <!-- don't boost on demo pages, sinon hijacks everything :/ -->
{% else %}
<div id="nav" class="10 col" kt-boost="true">
<div id="nav" class="10 col" hx-boost="true">
{% endif %}
<div class="row">
<div class="1 col">
@ -55,8 +55,8 @@
<div class="8 col">
</div>
<div class="8 col" style="text-align: right">
<a href="https://github.com/bigskysoftware/kutty">github</a>
<iframe style="margin:auto;" src="https://ghbtns.com/github-btn.html?user=bigskysoftware&repo=kutty&type=star&count=true" frameborder="0" scrolling="0" width="150" height="20" title="Star twbs/bootstrap on GitHub"></iframe>
<a href="https://github.com/bigskysoftware/htmx">github</a>
<iframe style="margin:auto;" src="https://ghbtns.com/github-btn.html?user=bigskysoftware&repo=htmx&type=star&count=true" frameborder="0" scrolling="0" width="150" height="20" title="Star twbs/bootstrap on GitHub"></iframe>
</div>
</div>
</div>

View File

@ -1,11 +1,11 @@
---
layout: layout.njk
title: </> kutty - kt-boost
title: </> htmx - hx-boost
---
## `kt-boost`
## `hx-boost`
The `kt-boost` attribute allows you to "boost" normal anchors and form tags to use AJAX instead. This
The `hx-boost` attribute allows you to "boost" normal anchors and form tags to use AJAX instead. This
has the [nice fallback](https://en.wikipedia.org/wiki/Progressive_enhancement) that, if the user does not
have javascript enabled, the site will continue to work.
@ -21,7 +21,7 @@ swap will be used.
Here is an example of some boosted links:
```html
<div kt-boost="true">
<div hx-boost="true">
<a href="/page1">Go To Page 1</a>
<a href="/page2">Go To Page 2</a>
</div>
@ -29,6 +29,6 @@ Here is an example of some boosted links:
### Notes
* `kt-boost` is inherited and can be placed on a parent element
* `hx-boost` is inherited and can be placed on a parent element
* Only links that are to the same domain and that are not local anchors will be boosted
* All requests are done via AJAX, so keep that in mind when doing things like redirects

View File

@ -1,15 +1,15 @@
---
layout: layout.njk
title: </> kutty - kt-classes
title: </> htmx - hx-classes
---
## `kt-classes`
## `hx-classes`
The `kt-classes` attribute allows you to specify CSS classes that will be swapped onto the element that
The `hx-classes` attribute allows you to specify CSS classes that will be swapped onto the element that
the attribute is on. This allows you to apply [CSS Transitions](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions)
to your HTML without resorting to javascript.
A `kt-classes` attribute value consists of "runs", which are separated by an `&` character. All
A `hx-classes` attribute value consists of "runs", which are separated by an `&` character. All
class operations within a given run will be applied sequentially, with the delay specified.
Within a run, a `,` character separates distinct class operations.
@ -20,16 +20,16 @@ optionally followed by a colon `:` and a time delay.
Here are some examples:
```html
<div kt-classes="add foo"/> <!-- adds the class "foo" after 100ms -->
<div kt-classes="remove bar:1s"/> <!-- removes the class "bar" after 1s -->
<div kt-classes="remove bar:1s, add foo:1s"/> <!-- removes the class "bar" after 1s
<div hx-classes="add foo"/> <!-- adds the class "foo" after 100ms -->
<div hx-classes="remove bar:1s"/> <!-- removes the class "bar" after 1s -->
<div hx-classes="remove bar:1s, add foo:1s"/> <!-- removes the class "bar" after 1s
then adds the class "foo" 1s after that -->
<div kt-classes="remove bar:1s & add foo:1s"/> <!-- removes the class "bar" and adds
<div hx-classes="remove bar:1s & add foo:1s"/> <!-- removes the class "bar" and adds
class "foo" after 1s -->
<div kt-classes="toggle foo:1s"/> <!-- toggles the class "foo" every 1s -->
<div hx-classes="toggle foo:1s"/> <!-- toggles the class "foo" every 1s -->
```
### Notes
* `kt-classes` is not inherited
* `hx-classes` is not inherited
* The default delay if none is specified is 100ms

View File

@ -1,21 +1,21 @@
---
layout: layout.njk
title: </> kutty - kt-confirm
title: </> htmx - hx-confirm
---
## `kt-confirm`
## `hx-confirm`
The `kt-confirm` attribute allows you to confirm an action before issuing a request. This can be useful
The `hx-confirm` attribute allows you to confirm an action before issuing a request. This can be useful
in cases where the action is destructive and you want to ensure that the user really wants to do it.
Here is an example:
```html
<button kt-delete="/account" kt-confirm="Are you sure you wish to delete your account?">
<button hx-delete="/account" hx-confirm="Are you sure you wish to delete your account?">
Delete My Account
</button>
```
### Notes
* `kt-confirm` is inherited and can be placed on a parent element
* `hx-confirm` is inherited and can be placed on a parent element

View File

@ -1,15 +1,15 @@
---
layout: layout.njk
title: </> kutty - kt-delete
title: </> htmx - hx-delete
---
## `kt-delete`
## `hx-delete`
The `kt-delete` attribute will cause an element to issue a `DELETE` to the specified URL and swap
The `hx-delete` attribute will cause an element to issue a `DELETE` to the specified URL and swap
the HTML into the DOM using a swap strategy:
```html
<button kt-delete="/account" kt-target="body">
<button hx-delete="/account" hx-target="body">
Delete Your Account
</button>
```
@ -19,9 +19,9 @@ This example will cause the `button` to issue a `DELETE` to `/account` and swap
### Notes
* `kt-delete` is not inherited
* `hx-delete` is not inherited
* Since most browsers do not support issuing an actual `DELETE`, the request will actually be issued
as a `POST`, with the [`X-HTTP-Method-Override`](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields) header set to `DELETE`.
* You can control the target of the swap using the [kt-target](/attributes/kt-target) attribute
* You can control the swap strategy by using the [kt-swap](/attributes/kt-swap) attribute
* You can control what event triggers the request with the [kt-trigger](/attributes/kt-trigger) attribute
* You can control the target of the swap using the [hx-target](/attributes/hx-target) attribute
* You can control the swap strategy by using the [hx-swap](/attributes/hx-swap) attribute
* You can control what event triggers the request with the [hx-trigger](/attributes/hx-trigger) attribute

View File

@ -0,0 +1,24 @@
---
layout: layout.njk
title: </> htmx - hx-error-url
---
## `hx-error-url`
The `hx-error-url` attribute allows you to send client-side errors to a specified URL. It is typically put on the
body tag, so all errors are caught and send to the server.
```html
<body hx-error-url="/errors">\
</body>
```
When a client side error is caught by htmx it will be `POST`-ed to the given URL, with the following JSON format:
```json
{ "elt": elt.id, "event": eventName, "detail" : detail }
```
### Notes
* `hx-error-url` is inherited and can be placed on a parent element

25
www/attributes/hx-get.md Normal file
View File

@ -0,0 +1,25 @@
---
layout: layout.njk
title: </> htmx - hx-get
---
## `hx-get`
The `hx-get` attribute will cause an element to issue a `GET` to the specified URL and swap
the HTML into the DOM using a swap strategy:
```html
<div hx-get="/example">Get Some HTML</div>
```
This example will cause the `div` to issue a `GET` to `/example` and swap the returned HTML into
the `innerHTML` of the `div`.
### Notes
* `hx-get` is not inherited
* By default `hx-get` does not include any parameters. You can use the [hx-params](/attributes/hx-params)
attribute to change this
* You can control the target of the swap using the [hx-target](/attributes/hx-target) attribute
* You can control the swap strategy by using the [hx-swap](/attributes/hx-swap) attribute
* You can control what event triggers the request with the [hx-trigger](/attributes/hx-trigger) attribute

View File

@ -1,14 +1,14 @@
---
layout: layout.njk
title: </> kutty - kt-history-elt
title: </> htmx - hx-history-elt
---
## `kt-history-elt`
## `hx-history-elt`
The `kt-history-elt` attribute allows you to specify the element that will be used to snapshot and
The `hx-history-elt` attribute allows you to specify the element that will be used to snapshot and
restore page state during navigation. By default, the `body` tag is used. This is typically
good enough for most setups, but you may want to narrow it down to a child element. Just make
sure that the element is always visible in your application, or kutty will not be able to restore
sure that the element is always visible in your application, or htmx will not be able to restore
history navigation properly.
@ -17,7 +17,7 @@ Here is an example:
```html
<html>
<body>
<div id="content" kt-history-elt>
<div id="content" hx-history-elt>
...
</div>
</body>
@ -26,5 +26,5 @@ Here is an example:
### Notes
* `kt-history-elt` is not inherited
* `hx-history-elt` is not inherited
* In most cases we don't recommend narrowing the history snapshot

View File

@ -1,18 +1,18 @@
---
layout: layout.njk
title: </> kutty - kt-include
title: </> htmx - hx-include
---
## `kt-include`
## `hx-include`
The `kt-include` attribute allows you to include additional element values in an AJAX request. The value of
The `hx-include` attribute allows you to include additional element values in an AJAX request. The value of
this attribute is a CSS query selector of the element or elements to include in the query.
Here is an example that includes a separate input value:
```html
<div>
<button kt-post="/register" kt-include="[name='email']">
<button hx-post="/register" hx-include="[name='email']">
Register!
</button>
Enter email: <input name="email" type="email"/>
@ -24,4 +24,4 @@ the value automatically, but it demonstrates the concept.
### Notes
* `kt-include` is inherited and can be placed on a parent element
* `hx-include` is inherited and can be placed on a parent element

View File

@ -1,11 +1,11 @@
---
layout: layout.njk
title: </> kutty - kt-indicator
title: </> htmx - hx-indicator
---
## `kt-indicator`
## `hx-indicator`
The `kt-indicator` attribute allows you to specify the element that will have the `kutty-request` class
The `hx-indicator` attribute allows you to specify the element that will have the `htmx-request` class
added to it for the duration of the request. This can be used to show spinners or progress indicators
while the request is in flight.
@ -15,26 +15,26 @@ Here is an example with a spinner adjacent to the button:
```html
<div>
<button kt-post="/example" kt-indicator="#spinner">
<button hx-post="/example" hx-indicator="#spinner">
Post It!
</button>
<img id="spinner" class="kutty-indicator" src="/img/bars.svg"/>
<img id="spinner" class="htmx-indicator" src="/img/bars.svg"/>
</div>
```
When a request is in flight, this will cause the `kutty-request` class to be added to the `#spinner`
image. The image also has the `kutty-indicator` class on it, which defines an opacity transition
When a request is in flight, this will cause the `htmx-request` class to be added to the `#spinner`
image. The image also has the `htmx-indicator` class on it, which defines an opacity transition
that will show the spinner:
```css
.kutty-indicator{
.htmx-indicator{
opacity:0;
transition: opacity 500ms ease-in;
}
.kutty-request .kutty-indicator{
.htmx-request .htmx-indicator{
opacity:1
}
.kutty-request.kutty-indicator{
.htmx-request.htmx-indicator{
opacity:1
}
```
@ -43,13 +43,13 @@ If you would prefer a different effect for showing the spinner you could define
CSS. Here is an example that uses `display` rather than opacity:
```css
.kutty-indicator{
.htmx-indicator{
display:none;
}
.kutty-request .my-indicator{
.htmx-request .my-indicator{
display:inline;
}
.kutty-request.my-indicator{
.htmx-request.my-indicator{
display:inline;
}
```
@ -57,14 +57,14 @@ CSS. Here is an example that uses `display` rather than opacity:
Note that the target of the `ic-indicator` selector need not be the exact element that you
want to show: it can be any element in the parent hierarchy of the indicator.
Finally, note that the `kutty-request` class by default is added to the element causing
Finally, note that the `htmx-request` class by default is added to the element causing
the request, so you can place an indicator inside of that element and not need to explictly
call it out with the `ic-indicator` attribute:
```html
<button kt-post="/example">
<button hx-post="/example">
Post It!
<img class="kutty-indicator" src="/img/bars.svg"/>
<img class="htmx-indicator" src="/img/bars.svg"/>
</button>
```
@ -72,13 +72,13 @@ call it out with the `ic-indicator` attribute:
This simulates what a spinner might look like in that situation:
<button class="btn" kt-classes="toggle kutty-request:3s">
<button class="btn" hx-classes="toggle htmx-request:3s">
Post It!
<img class="kutty-indicator" src="/img/bars.svg"/>
<img class="htmx-indicator" src="/img/bars.svg"/>
</button>
### Notes
* `kt-indicator` is inherited and can be placed on a parent element
* In the absence of an explicit indicator, the `kutty-request` class will be added to the element triggering the
* `hx-indicator` is inherited and can be placed on a parent element
* In the absence of an explicit indicator, the `htmx-request` class will be added to the element triggering the
request

View File

@ -1,11 +1,11 @@
---
layout: layout.njk
title: </> kutty - kt-params
title: </> htmx - hx-params
---
## `kt-params`
## `hx-params`
The `kt-params` attribute allows you to filter the parameters that will be submitted with an AJAX request.
The `hx-params` attribute allows you to filter the parameters that will be submitted with an AJAX request.
The possible values of this attribute are:
@ -15,7 +15,7 @@ The possible values of this attribute are:
* `<param-list>` - Include all the comma separated list of parameter names
```html
<div kt-get="/example" kt-params="*">Get Some HTML, Including Params</div>
<div hx-get="/example" hx-params="*">Get Some HTML, Including Params</div>
```
This div will include all the parameters that a `POST` would, but they will be URL encoded
@ -23,4 +23,4 @@ and included in the URL, as per usual with a `GET`.
### Notes
* `kt-params` is inherited and can be placed on a parent element
* `hx-params` is inherited and can be placed on a parent element

View File

@ -1,15 +1,15 @@
---
layout: layout.njk
title: </> kutty - kt-patch
title: </> htmx - hx-patch
---
## `kt-patch`
## `hx-patch`
The `kt-patch` attribute will cause an element to issue a `PATCH` to the specified URL and swap
The `hx-patch` attribute will cause an element to issue a `PATCH` to the specified URL and swap
the HTML into the DOM using a swap strategy:
```html
<button kt-patch="/account" kt-target="body">
<button hx-patch="/account" hx-target="body">
Patch Your Account
</button>
```
@ -19,9 +19,9 @@ This example will cause the `button` to issue a `PATCH` to `/account` and swap t
### Notes
* `kt-patch` is not inherited
* `hx-patch` is not inherited
* Since most browsers do not support issuing an actual `PATCH`, the request will actually be issued
as a `POST`, with the [`X-HTTP-Method-Override`](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields) header set to `PATCH`.
* You can control the target of the swap using the [kt-target](/attributes/kt-target) attribute
* You can control the swap strategy by using the [kt-swap](/attributes/kt-swap) attribute
* You can control what event triggers the request with the [kt-trigger](/attributes/kt-trigger) attribute
* You can control the target of the swap using the [hx-target](/attributes/hx-target) attribute
* You can control the swap strategy by using the [hx-swap](/attributes/hx-swap) attribute
* You can control what event triggers the request with the [hx-trigger](/attributes/hx-trigger) attribute

25
www/attributes/hx-post.md Normal file
View File

@ -0,0 +1,25 @@
---
layout: layout.njk
title: </> htmx - hx-post
---
## `hx-post`
The `hx-post` attribute will cause an element to issue a `POST` to the specified URL and swap
the HTML into the DOM using a swap strategy:
```html
<button hx-post="/account/enable" hx-target="body">
Enable Your Account
</button>
```
This example will cause the `button` to issue a `POST` to `/account/enable` and swap the returned HTML into
the `innerHTML` of the `body`.
### Notes
* `hx-post` is not inherited
* You can control the target of the swap using the [hx-target](/attributes/hx-target) attribute
* You can control the swap strategy by using the [hx-swap](/attributes/hx-swap) attribute
* You can control what event triggers the request with the [hx-trigger](/attributes/hx-trigger) attribute

View File

@ -0,0 +1,21 @@
---
layout: layout.njk
title: </> htmx - hx-prompt
---
## `hx-prompt`
The `hx-prompt` attribute allows you to show a prompt before issuing a request. The value of
the prompt will be included in the requst in the `X-HX-Prompt` header.
Here is an example:
```html
<button hx-delete="/account" hx-prompt="Enter your account name to confirm deletion">
Delete My Account
</button>
```
### Notes
* `hx-prompt` is inherited and can be placed on a parent element

View File

@ -0,0 +1,26 @@
---
layout: layout.njk
title: </> htmx - hx-push-url
---
## `hx-push-url`
The `hx-push-url` attribute allows you to "push" a new entry into the browser location bar, which creates
a new history entry, allowing back-button and general history navigation. The possible values of this
attribute are `true` and `false`.
Here is an example:
```html
<div hx-get="/account" hx-push-url="true">
Go to My Account
</div>
```
This will cause htmx to snapshot the current DOM to `localStorage` and push the URL `/account' into the browser
location bar.
### Notes
* `hx-push-url` is inherited and can be placed on a parent element
* see also the `X-HX-Push` response header

View File

@ -1,15 +1,15 @@
---
layout: layout.njk
title: </> kutty - kt-put
title: </> htmx - hx-put
---
## `kt-put`
## `hx-put`
The `kt-put` attribute will cause an element to issue a `PUT` to the specified URL and swap
The `hx-put` attribute will cause an element to issue a `PUT` to the specified URL and swap
the HTML into the DOM using a swap strategy:
```html
<button kt-put="/account" kt-target="body">
<button hx-put="/account" hx-target="body">
Put Money In Your Account
</button>
```
@ -19,9 +19,9 @@ This example will cause the `button` to issue a `PUT` to `/account` and swap the
### Notes
* `kt-put` is not inherited
* `hx-put` is not inherited
* Since most browsers do not support issuing an actual `PUT`, the request will actually be issued
as a `POST`, with the [`X-HTTP-Method-Override`](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields) header set to `PUT`.
* You can control the target of the swap using the [kt-target](/attributes/kt-target) attribute
* You can control the swap strategy by using the [kt-swap](/attributes/kt-swap) attribute
* You can control what event triggers the request with the [kt-trigger](/attributes/kt-trigger) attribute
* You can control the target of the swap using the [hx-target](/attributes/hx-target) attribute
* You can control the swap strategy by using the [hx-swap](/attributes/hx-swap) attribute
* You can control what event triggers the request with the [hx-trigger](/attributes/hx-trigger) attribute

View File

@ -1,18 +1,18 @@
---
layout: layout.njk
title: </> kutty - kt-select
title: </> htmx - hx-select
---
## `kt-select`
## `hx-select`
The `kt-select` attribute allows you to select the content you want swapped from a response. The value of
The `hx-select` attribute allows you to select the content you want swapped from a response. The value of
this attribute is a CSS query selector of the element or elements to select from the response.
Here is an example that selects a subset of the response content:
```html
<div>
<button kt-get="/info" kt-select="#info-details" kt-swap="outerHTML">
<button hx-get="/info" hx-select="#info-details" hx-swap="outerHTML">
Get Info!
</button>
</div>
@ -23,4 +23,4 @@ which will replace the entire button in the DOM.
### Notes
* `kt-select` is inherited and can be placed on a parent element
* `hx-select` is inherited and can be placed on a parent element

View File

@ -1,16 +1,16 @@
---
layout: layout.njk
title: </> kutty - kt-sse-src
title: </> htmx - hx-sse-src
---
## `kt-sse-src`
## `hx-sse-src`
The `kt-sse-src` attribute establishes a [Server Sent Event](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events)
The `hx-sse-src` attribute establishes a [Server Sent Event](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events)
`EventSource`, allowing children of the element to register for server sent event triggers.
```html
<div kt-sse-src="/event_stream">
<div kt-get="/chatroom" kt-trigger="sse:chatter">
<div hx-sse-src="/event_stream">
<div hx-get="/chatroom" hx-trigger="sse:chatter">
...
</div>
</div>
@ -21,4 +21,4 @@ a `GET` to the `/chatroom` url whenever the `chatter` event is seen.
### Notes
* `kt-sse-src` is not inherited
* `hx-sse-src` is not inherited

View File

@ -1,11 +1,11 @@
---
layout: layout.njk
title: </> kutty - kt-swap-oob
title: </> htmx - hx-swap-oob
---
## `kt-swap-oob`
## `hx-swap-oob`
The `kt-swap-oob` attribute allows you specify that some content in a response should be swapped into
The `hx-swap-oob` attribute allows you specify that some content in a response should be swapped into
the DOM somewhere other than the target, that is "Out of Band". This allows you to piggy back updates
to other element updates on a response.
@ -15,7 +15,7 @@ Consider the following response HTML:
<div>
...
</div>
<div id="alerts" kt-swap-oob="true">
<div id="alerts" hx-swap-oob="true">
Saved!
</div>
@ -26,5 +26,5 @@ as a replacement for the element with the id `alerts`, and will not end up in th
### Notes
* `kt-swap-oob` is not inherited
* `kt-swap-oob` is only supported on top level elements in the response, not children
* `hx-swap-oob` is not inherited
* `hx-swap-oob` is only supported on top level elements in the response, not children

View File

@ -1,12 +1,12 @@
---
layout: layout.njk
title: </> kutty - kt-swap
title: </> htmx - hx-swap
---
## `kt-swap`
## `hx-swap`
The `kt-swap` attribute allows you to specify how the response will be swapped in relative to the
[target](/attributes/kt-target) of an AJAX request.
The `hx-swap` attribute allows you to specify how the response will be swapped in relative to the
[target](/attributes/hx-target) of an AJAX request.
The possible values of this attribute are:
@ -24,17 +24,17 @@ specification.
So in this code:
```html
<div kt-get="/example" kt-swap="afterend">Get Some HTML & Append It</div>
<div hx-get="/example" hx-swap="afterend">Get Some HTML & Append It</div>
```
The `div` will issue a request to `/example` and append the returned content after the `div`
You can modify the amount of time that kutty will wait after receiving a response to swap the content
You can modify the amount of time that htmx will wait after receiving a response to swap the content
by including a `swap` modifier:
```html
<!-- this will wait 1s before doing the swap after it is received -->
<div kt-get="/example" kt-swap="innerHTML swap:1s">Get Some HTML & Append It</div>
<div hx-get="/example" hx-swap="innerHTML swap:1s">Get Some HTML & Append It</div>
```
Similarly, you can modify the time between the swap and the settle logic by including a `settle`
@ -42,14 +42,14 @@ modifier:
```html
<!-- this will wait 1s before doing the swap after it is received -->
<div kt-get="/example" kt-swap="innerHTML settle:1s">Get Some HTML & Append It</div>
<div hx-get="/example" hx-swap="innerHTML settle:1s">Get Some HTML & Append It</div>
```
These attributes can be used to synchronize kutty with the timing of CSS transition effects.
These attributes can be used to synchronize htmx with the timing of CSS transition effects.
### Notes
* `kt-swap` is inherited and can be placed on a parent element
* `hx-swap` is inherited and can be placed on a parent element
* The default value of this attribute is `innerHTML`
* The default swap delay is 0ms
* The default settle delay is 100ms

View File

@ -1,11 +1,11 @@
---
layout: layout.njk
title: </> kutty - kt-target
title: </> htmx - hx-target
---
## `kt-target`
## `hx-target`
The `kt-target` attribute allows you to target a different element for swapping than the one issuing the AJAX
The `hx-target` attribute allows you to target a different element for swapping than the one issuing the AJAX
request. The value of this attribute can be:
* a CSS query selector of the element to target
@ -18,7 +18,7 @@ Here is an example that targets a div:
```html
<div>
<div id="response-div"></div>
<button kt-post="/register" kt-target="#response-div" kt-swap="beforeEnd">
<button hx-post="/register" hx-target="#response-div" hx-swap="beforeEnd">
Register!
</button>
</div>
@ -28,4 +28,4 @@ The response from the `/register` url will be appended to the `div` with the id
### Notes
* `kt-target` is inherited and can be placed on a parent element
* `hx-target` is inherited and can be placed on a parent element

View File

@ -1,11 +1,11 @@
---
layout: layout.njk
title: </> kutty - kt-trigger
title: </> htmx - hx-trigger
---
## `kt-trigger`
## `hx-trigger`
The `kt-trigger` attribute allows you to specify what triggers an AJAX request. A trigger
The `hx-trigger` attribute allows you to specify what triggers an AJAX request. A trigger
value can be one of the following:
* An event name (e.g. "click") followed by a set of event modifiers
@ -17,7 +17,7 @@ value can be one of the following:
A standard event, such as `click` can be specified as the trigger like so:
```html
<div kt-get="/clicked" kt-trigger="click">Click Me</div>
<div hx-get="/clicked" hx-trigger="click">Click Me</div>
```
Standard events can also have modifiers that change how they behave. The modifiers are:
@ -32,13 +32,13 @@ and the user hasn't typed anything new for 1 second:
```html
<input name="q"
kt-get="/search" kt-trigger="keyup changed delay:1s"
kt-target="#search-results"/>
hx-get="/search" hx-trigger="keyup changed delay:1s"
hx-target="#search-results"/>
```
The response from the `/register` url will be appended to the `div` with the id `response-div`.
There are two special events that are non-standard that kutty supports:
There are two special events that are non-standard that htmx supports:
* `load` - triggered on load (useful for lazy-loading something)
* `reveal` - triggered when an element is scrolled into the viewport (also useful for lazy-loading)
@ -48,7 +48,7 @@ There are two special events that are non-standard that kutty supports:
By using the syntax `every <timing declaration>` you can have an element poll periodically:
```html
<div kt-get="/latest_updates" kt-trigger="every 1s">
<div hx-get="/latest_updates" hx-trigger="every 1s">
Nothing Yet!
</div>
```
@ -64,8 +64,8 @@ an element can register to be triggered by a specific SSE event using the syntax
Here is an example:
```html
<div kt-sse-src="/event_stream">
<div kt-get="/chatroom" kt-trigger="sse:chatter">
<div hx-sse-src="/event_stream">
<div hx-get="/chatroom" hx-trigger="sse:chatter">
...
</div>
</div>
@ -76,4 +76,4 @@ a `GET` to the `/chatroom` url whenever the `chatter` event is seen.
### Notes
* `kt-trigger` is not inherited
* `hx-trigger` is not inherited

View File

@ -1,24 +0,0 @@
---
layout: layout.njk
title: </> kutty - kt-error-url
---
## `kt-error-url`
The `kt-error-url` attribute allows you to send client-side errors to a specified URL. It is typically put on the
body tag, so all errors are caught and send to the server.
```html
<body kt-error-url="/errors">\
</body>
```
When a client side error is caught by kutty it will be `POST`-ed to the given URL, with the following JSON format:
```json
{ "elt": elt.id, "event": eventName, "detail" : detail }
```
### Notes
* `kt-error-url` is inherited and can be placed on a parent element

View File

@ -1,25 +0,0 @@
---
layout: layout.njk
title: </> kutty - kt-get
---
## `kt-get`
The `kt-get` attribute will cause an element to issue a `GET` to the specified URL and swap
the HTML into the DOM using a swap strategy:
```html
<div kt-get="/example">Get Some HTML</div>
```
This example will cause the `div` to issue a `GET` to `/example` and swap the returned HTML into
the `innerHTML` of the `div`.
### Notes
* `kt-get` is not inherited
* By default `kt-get` does not include any parameters. You can use the [kt-params](/attributes/kt-params)
attribute to change this
* You can control the target of the swap using the [kt-target](/attributes/kt-target) attribute
* You can control the swap strategy by using the [kt-swap](/attributes/kt-swap) attribute
* You can control what event triggers the request with the [kt-trigger](/attributes/kt-trigger) attribute

View File

@ -1,25 +0,0 @@
---
layout: layout.njk
title: </> kutty - kt-post
---
## `kt-post`
The `kt-post` attribute will cause an element to issue a `POST` to the specified URL and swap
the HTML into the DOM using a swap strategy:
```html
<button kt-post="/account/enable" kt-target="body">
Enable Your Account
</button>
```
This example will cause the `button` to issue a `POST` to `/account/enable` and swap the returned HTML into
the `innerHTML` of the `body`.
### Notes
* `kt-post` is not inherited
* You can control the target of the swap using the [kt-target](/attributes/kt-target) attribute
* You can control the swap strategy by using the [kt-swap](/attributes/kt-swap) attribute
* You can control what event triggers the request with the [kt-trigger](/attributes/kt-trigger) attribute

View File

@ -1,21 +0,0 @@
---
layout: layout.njk
title: </> kutty - kt-prompt
---
## `kt-prompt`
The `kt-prompt` attribute allows you to show a prompt before issuing a request. The value of
the prompt will be included in the requst in the `X-KT-Prompt` header.
Here is an example:
```html
<button kt-delete="/account" kt-prompt="Enter your account name to confirm deletion">
Delete My Account
</button>
```
### Notes
* `kt-prompt` is inherited and can be placed on a parent element

View File

@ -1,26 +0,0 @@
---
layout: layout.njk
title: </> kutty - kt-push-url
---
## `kt-push-url`
The `kt-push-url` attribute allows you to "push" a new entry into the browser location bar, which creates
a new history entry, allowing back-button and general history navigation. The possible values of this
attribute are `true` and `false`.
Here is an example:
```html
<div kt-get="/account" kt-push-url="true">
Go to My Account
</div>
```
This will cause kutty to snapshot the current DOM to `localStorage` and push the URL `/account' into the browser
location bar.
### Notes
* `kt-push-url` is inherited and can be placed on a parent element
* see also the `X-KT-Push` response header

View File

@ -1,5 +1,5 @@
/**
* kutty theme for JavaScript, CSS and HTML
* htmx theme for JavaScript, CSS and HTML
* based on okaidia theme by ocodia
*/

View File

@ -1,6 +1,6 @@
---
layout: layout.njk
title: </> kutty - high power tools for html
title: </> htmx - high power tools for html
---
<div class="row">
<div class="2 col nav">
@ -32,12 +32,12 @@ title: </> kutty - high power tools for html
</div>
<div class="10 col">
## <a name="introduction"></a>[Kutty in a Nutshell](#introduction)
## <a name="introduction"></a>[Htmx in a Nutshell](#introduction)
Kutty is a library that allows you to access modern browser features directly from HTML, rather than using
Htmx is a library that allows you to access modern browser features directly from HTML, rather than using
javascript.
To understand kutty, first lets take a look at an anchor tag:
To understand htmx, first lets take a look at an anchor tag:
``` html
<a href="/blog">Blog</a>
@ -51,20 +51,20 @@ This anchor tag tells a browser:
With that in mind, consider the following bit of HTML:
``` html
<div kt-post="/clicked"
kt-trigger="click"
kt-target="#parent-div"
kt-swap="outerHTML">
<div hx-post="/clicked"
hx-trigger="click"
hx-target="#parent-div"
hx-swap="outerHTML">
Click Me!
</div>
```
This tells kutty:
This tells htmx:
> "When a user clicks on this div, issue an HTTP POST request to '/clicked' and use the content from the response
> to replace the element with the id `parent-div` in the DOM"
Kutty extends and generalizes the core idea of HTML as a hypertext, opening up many more possibilities directly
Htmx extends and generalizes the core idea of HTML as a hypertext, opening up many more possibilities directly
within the language:
* Now any element, not just anchors and forms, can issue an HTTP request
@ -72,37 +72,37 @@ within the language:
* Now any [HTTP verb](https://en.wikipedia.org/wiki/HTTP_Verbs), not just `GET` and `POST`, can be used
* Now any element, not just the entire window, can be the target for update by the request
Note that when you are using kutty, on the server side you respond with *HTML*, not *JSON*. This keeps you firmly
Note that when you are using htmx, on the server side you respond with *HTML*, not *JSON*. This keeps you firmly
within the [original web programming model]((https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm)),
using [Hypertext As The Engine Of Application State](https://en.wikipedia.org/wiki/HATEOAS)
without even needing to really understand that concept.
It's worth mentioning that, if you prefer, you can use the `data-` prefix when using kutty:
It's worth mentioning that, if you prefer, you can use the `data-` prefix when using htmx:
``` html
<a data-kt-post="/click">Click Me!</a>
<a data-hx-post="/click">Click Me!</a>
```
## <a name="installing"></a> [Installing](#installing)
Kutty is a dependency-free javascript library.
Htmx is a dependency-free javascript library.
It can be used via [NPM](https://www.npmjs.com/) as "`kutty.org`" or downloaded or included from
[unpkg](https://unpkg.com/browse/kutty.org/) or your other favorite NPM-based CDN:
It can be used via [NPM](https://www.npmjs.com/) as "`htmx.org`" or downloaded or included from
[unpkg](https://unpkg.com/browse/htmx.org/) or your other favorite NPM-based CDN:
``` html
<script src="https://unpkg.com/kutty.org@0.0.2"></script>
<script src="https://unpkg.com/htmx.org@0.0.2"></script>
```
## <a name="ajax"></a> [AJAX](#ajax)
The core feature of kutty is a set of attributes that allow you to issue AJAX requests directly from HTML:
The core feature of htmx is a set of attributes that allow you to issue AJAX requests directly from HTML:
* [kt-get](/attributes/kt-get) - Issues a `GET` request to the given URL
* [kt-post](/attributes/kt-post) - Issues a `POST` request to the given URL
* [kt-put](/attributes/kt-put) - Issues a `PUT` request to the given URL
* [kt-patch](/attributes/kt-patch) - Issues a `PATCH` request to the given URL
* [kt-delete](/attributes/kt-delete) - Issues a `DELETE` request to the given URL
* [hx-get](/attributes/hx-get) - Issues a `GET` request to the given URL
* [hx-post](/attributes/hx-post) - Issues a `POST` request to the given URL
* [hx-put](/attributes/hx-put) - Issues a `PUT` request to the given URL
* [hx-patch](/attributes/hx-patch) - Issues a `PATCH` request to the given URL
* [hx-delete](/attributes/hx-delete) - Issues a `DELETE` request to the given URL
(Since most browsers only support issuing `GET` and `POST`, a request with one of the other three methods will
actually be issued as a `POST`, with the `X-HTTP-Method-Override` header set to the desired method.)
@ -111,7 +111,7 @@ Each of these attributes takes a URL to issue an AJAX request to. The element w
type to the given URL when the element is [triggered](#triggers):
```html
<div kt-put="/messages">
<div hx-put="/messages">
Put To Messages
</div>
```
@ -128,13 +128,13 @@ By default, AJAX requests are triggered by the "natural" event of an element:
* `form` is triggered on the `submit` event
* everything else is triggered by the `click` event
If you want different behavior you can use the [kt-trigger](/attributes/kt-trigger)
If you want different behavior you can use the [hx-trigger](/attributes/hx-trigger)
attribute to specify which event will cause the request.
Here is a `div` that posts to `/mouse_entered` when a mouse enters it:
```html
<div kt-post="/mouse_entered" kt-trigger="mouseenter">
<div hx-post="/mouse_entered" hx-trigger="mouseenter">
[Here Mouse, Mouse!]
</div>
```
@ -142,7 +142,7 @@ Here is a `div` that posts to `/mouse_entered` when a mouse enters it:
If you want a request to only happen once, you can use the `once` modifier for the trigger:
```html
<div kt-post="/mouse_entered" kt-trigger="mouseenter once"">
<div hx-post="/mouse_entered" hx-trigger="mouseenter once"">
[Here Mouse, Mouse!]
</div>
```
@ -157,9 +157,9 @@ You can use these two attributes to implement a common UX pattern, [Active Searc
```html
<input type="text" name="q"
kt-get="/trigger_delay"
kt-trigger="keyup changed delay:500ms"
kt-target="#search-results"
hx-get="/trigger_delay"
hx-trigger="keyup changed delay:500ms"
hx-target="#search-results"
placeholder="Search..."/>
<div id="search-results"></div>
```
@ -169,7 +169,7 @@ into the `div` with the id `search-results`.
#### <a name="special-events"></a> [Special Events](#special-events)
kutty provides a few special events for use in [kt-trigger](/attributes/kt-trigger):
htmx provides a few special events for use in [hx-trigger](/attributes/hx-trigger):
* `load` - fires once when the element is first loaded
* `revealed` - fires once when an element first scrolls into the viewport
@ -179,14 +179,14 @@ You can also use custom events to trigger requests if you have an advanced use c
#### <a name="polling"></a> [Polling](#polling)
If you want an element to poll the given URL rather than wait for an event, you can use the `every` syntax
with the [`kt-trigger`](/attributes/kt-trigger/) attribute:
with the [`hx-trigger`](/attributes/hx-trigger/) attribute:
```html
<div kt-get="/news" trigger="every 2s">
<div hx-get="/news" trigger="every 2s">
</div>
```
This tells kutty
This tells htmx
> Every 2 seconds, issue a GET to /news and load the response into the div
@ -195,13 +195,13 @@ and the element will cancel the polling.
#### <a name="load_polling"></a> [Load Polling](#load_polling)
Another technique that can be used to achieve polling in kutty is "load polling", where an element specifies
Another technique that can be used to achieve polling in htmx is "load polling", where an element specifies
an `load` trigger along with a delay, and replaces itself with the response:
```html
<div kt-get="/messages"
kt-trigger="load delay:1s"
kt-swap="outerHTML">
<div hx-get="/messages"
hx-trigger="load delay:1s"
hx-swap="outerHTML">
</div>
```
@ -218,9 +218,9 @@ when you are showing the user a [progress bar](/examples/progress-bar).
a way for servers to send events to browsers. It provides a higher-level mechanism for communication between the
server and the browser than websockets.
If you want an element to respond to a Server Sent Event via kutty, you need to do two things:
If you want an element to respond to a Server Sent Event via htmx, you need to do two things:
1. Define an SSE source. To do this, add a [kt-sse-src](/attributes/kt-sse-src) attribute on a parent element
1. Define an SSE source. To do this, add a [hx-sse-src](/attributes/hx-sse-src) attribute on a parent element
that specifies the URL from which Server Sent Events will be received.
2. Specify the Server Sent Event that will trigger the element, with the prefix `sse:`
@ -228,8 +228,8 @@ that specifies the URL from which Server Sent Events will be received.
Here is an example:
```html
<body kt-sse-src="/sse_messages">
<div trigger="sse:new_news" kt-get="/news"></div>
<body hx-sse-src="/sse_messages">
<div trigger="sse:new_news" hx-get="/news"></div>
</body>
```
@ -239,49 +239,49 @@ notify the div if there was new news to get, rather than the steady requests tha
### <a name="indicators"></a> [Request Indicators](#indicators)
When an AJAX request is issued it is often good to let the user know that something is happening since the browser
will not give them any feedback. You can accomplish this in kutty by using `kutty-indicator` class.
will not give them any feedback. You can accomplish this in htmx by using `htmx-indicator` class.
The `kutty-indicator` class is defined so that the opacity of any element with this class is 0 by default, making it invisible
The `htmx-indicator` class is defined so that the opacity of any element with this class is 0 by default, making it invisible
but present in the DOM.
When kutty issues a request, it will put a `kutty-request` class onto an element (either the requesting element or
another element, if specified). The `kutty-request` class will cause a child element with the `kutty-indicator` class
When htmx issues a request, it will put a `htmx-request` class onto an element (either the requesting element or
another element, if specified). The `htmx-request` class will cause a child element with the `htmx-indicator` class
on it to transition to an opacity of 1, showing the indicator.
```html
<button kt-get="/click">
<button hx-get="/click">
Click Me!
<img class="kutty-indicator" src="/spinner.gif"/>
<img class="htmx-indicator" src="/spinner.gif"/>
</button>
```
Here we have a button. When it is clicked the `kutty-request` class will be added to it, which will reveal the spinner
Here we have a button. When it is clicked the `htmx-request` class will be added to it, which will reveal the spinner
gif element. (I like [SVG spinners](http://samherbert.net/svg-loaders/) these days.)
While the `kutty-indicator` class uses opacity to hide and show the progress indicator, if you would prefer another mechanism
While the `htmx-indicator` class uses opacity to hide and show the progress indicator, if you would prefer another mechanism
you can create your own CSS transition like so:
```css
.kutty-indicator{
.htmx-indicator{
display:none;
}
.kutty-request .my-indicator{
.htmx-request .my-indicator{
display:inline;
}
.kutty-request.my-indicator{
.htmx-request.my-indicator{
display:inline;
}
```
If you want the `kutty-request` class added to a different element, you can use the [kt-indicator](/attributes/kt-indicator)
If you want the `htmx-request` class added to a different element, you can use the [hx-indicator](/attributes/hx-indicator)
attribute with a CSS selector to do so:
```html
<div>
<button kt-get="/click" kt-indicator="#indicator">
<button hx-get="/click" hx-indicator="#indicator">
Click Me!
</button>
<img id="indicator" class="kutty-indicator" src="/spinner.gif"/>
<img id="indicator" class="htmx-indicator" src="/spinner.gif"/>
</div>
```
@ -291,13 +291,13 @@ and had the same effect.
### <a name="targets"></a> [Targets](#targets)
If you want the response to be loaded into a different element other than the one that made the request, you can
use the [kt-target](/attributes/kt-target) attribute, which takes a CSS selector. Looking back at our Live Search example:
use the [hx-target](/attributes/hx-target) attribute, which takes a CSS selector. Looking back at our Live Search example:
```html
<input type="text" name="q"
kt-get="/trigger_delay"
kt-trigger="keyup delay:500ms changed"
kt-target="#search-results"
hx-get="/trigger_delay"
hx-trigger="keyup delay:500ms changed"
hx-target="#search-results"
placeholder="Search..."/>
<div id="search-results"></div>
```
@ -307,8 +307,8 @@ input tag.
### <a name="swapping"></a> [Swapping](#swapping)
kutty offers a few different ways to swap the HTML returned into the DOM. By default, the content replaces the
`innerHTML` of the target element. You can modify this by using the [kt-swap](/attributes/kt-swap) attribute
htmx offers a few different ways to swap the HTML returned into the DOM. By default, the content replaces the
`innerHTML` of the target element. You can modify this by using the [hx-swap](/attributes/hx-swap) attribute
with any of the following values:
* `innerHTML` - the default, puts the content inside the target element
@ -321,10 +321,10 @@ with any of the following values:
#### <a name="oob_swaps"></a>[Out of Band Swaps](#oob_swaps)
If you want to swap content from a response directly into the DOM by using the `id` attribute you can use the
[kt-swap-oob](/attributes/kt-swap-oob) attribute in the *response* html:
[hx-swap-oob](/attributes/hx-swap-oob) attribute in the *response* html:
```html
<div id="message" kt-swap-oob="true">Swap me directly!</div>
<div id="message" hx-swap-oob="true">Swap me directly!</div>
Additional Content
```
@ -337,7 +337,7 @@ Note that out of band elements must be in the top level of the response, and not
#### Selecting Content To Swap
If you want to select a subset of the response HTML to swap into the target, you can use the [kt-select](/attributes/kt-select)
If you want to select a subset of the response HTML to swap into the target, you can use the [hx-select](/attributes/hx-select)
attribute, which takes a CSS selector and selects the matching elements from the response.
### <a name="parameters"></a> [Parameters](#parameters)
@ -348,153 +348,153 @@ will include the values of all inputs within it.
Additionally, if the element causes a non-`GET` request, the values of all the inputs of the nearest enclosing form
will be included.
If you wish to include the values of other elements, you can use the [kt-include](/attributes/kt-include) attribute
If you wish to include the values of other elements, you can use the [hx-include](/attributes/hx-include) attribute
with a CSS selector of all the elements whose values you want to include in the request.
If you wish to filter out some parameters you can use the [kt-params](/attributes/kt-params) attribute.
If you wish to filter out some parameters you can use the [hx-params](/attributes/hx-params) attribute.
Finally, if you want to programatically modify the parameters, you can use the [configRequest.kutty](/events#configRequest.kutty)
Finally, if you want to programatically modify the parameters, you can use the [configRequest.htmx](/events#configRequest.htmx)
event.
## <a name="history"></a> [History Support](#history)
Kutty provides a simple mechanism for interacting with the [browser history API](https://developer.mozilla.org/en-US/docs/Web/API/History_API):
Htmx provides a simple mechanism for interacting with the [browser history API](https://developer.mozilla.org/en-US/docs/Web/API/History_API):
If you want a given element to push its request URL into the browser navigation bar and add the current state of the page
to the browser's history, include the [kt-push](/attributes/kt-push) attribute:
to the browser's history, include the [hx-push](/attributes/hx-push) attribute:
```html
<a kt-get="/blog" kt-push="true">Blog</a>
<a hx-get="/blog" hx-push="true">Blog</a>
```
When a user clicks on this link, kutty will snapshot the current DOM and store it before it makes a request to /blog.
When a user clicks on this link, htmx will snapshot the current DOM and store it before it makes a request to /blog.
It then does the swap and pushes a new location onto the history stack.
When a user hits the back button, kutty will retrieve the old content from storage and swap it back into the target,
When a user hits the back button, htmx will retrieve the old content from storage and swap it back into the target,
simulating "going back" to the previous state.
### Specifying History Snapshot Element
By default, kutty will use the `body` to take and restore the history snapshop from. This is usually the right thing, but
if you want to use a narrower element for snapshotting you can use the [kt-history-element](/attributes/kt-history-element)
By default, htmx will use the `body` to take and restore the history snapshop from. This is usually the right thing, but
if you want to use a narrower element for snapshotting you can use the [hx-history-element](/attributes/hx-history-element)
attribute to specify a different one.
Careful: this element will need to be on all pages or restoring from history won't work reliably.
## <a name="requests">[Requests &amp; Responses](#requests)
Kutty expects responses to the AJAX requests it makes to be HTML, typically HTML fragments (although a full HTML
document, matched with a [kt-select](/attributes/kt-select) tag can be useful too). Kutty will then swap the returned
Htmx expects responses to the AJAX requests it makes to be HTML, typically HTML fragments (although a full HTML
document, matched with a [hx-select](/attributes/hx-select) tag can be useful too). Htmx will then swap the returned
HTML into the document at the target specified and with the swap strategy specified.
Sometimes you might want to do nothing in the swap, but still perhaps trigger a client side event ([see below](#response-headers)).
For this situation you can return a `204 - No Content` response code, and kutty will ignore the content of the response.
For this situation you can return a `204 - No Content` response code, and htmx will ignore the content of the response.
In the event of an error response from the server (e.g. a 404 or a 501), kutty will trigger the [`responseError.kutty`](/events#responseError.kutty)
In the event of an error response from the server (e.g. a 404 or a 501), htmx will trigger the [`responseError.htmx`](/events#responseError.htmx)
event, which you can handle.
In the event of a connection error, the `sendError.kutty` event will be triggered.
In the event of a connection error, the `sendError.htmx` event will be triggered.
### <a name="request-header"></a> [Request Headers](#request-headers)
kutty includes a number of useful headers in requests:
htmx includes a number of useful headers in requests:
* `X-KT-Request` - will be set to "true"
* `X-KT-Trigger` - will be set to the id of the element that triggered the request
* `X-KT-Trigger-Name` - will be set to the name of the element that triggered the request
* `X-KT-Target` - will be set to the id of the target element
* `X-KT-Current-URL` - will be set to the URL of the browser
* `X-KT-Prompt` - will be set to the value entered by the user when prompted via [kt-prompt](/attributes/kt-prompt)
* `X-KT-Event-Target` - the id of the original target of the event that triggered the request
* `X-KT-Active-Element` - the id of the current active element
* `X-KT-Active-Element-Name` - the name of the current active element
* `X-KT-Active-Element-Value` - the value of the current active element
* `X-HX-Request` - will be set to "true"
* `X-HX-Trigger` - will be set to the id of the element that triggered the request
* `X-HX-Trigger-Name` - will be set to the name of the element that triggered the request
* `X-HX-Target` - will be set to the id of the target element
* `X-HX-Current-URL` - will be set to the URL of the browser
* `X-HX-Prompt` - will be set to the value entered by the user when prompted via [hx-prompt](/attributes/hx-prompt)
* `X-HX-Event-Target` - the id of the original target of the event that triggered the request
* `X-HX-Active-Element` - the id of the current active element
* `X-HX-Active-Element-Name` - the name of the current active element
* `X-HX-Active-Element-Value` - the value of the current active element
* `X-HTTP-Method-Override` - the HTTP verb for non-`GET` and `POST` requests
### <a name="response-header"></a> [Response Headers](#response-headers)
kutty supports two special response headers:
htmx supports two special response headers:
* `X-KT-Trigger` - can be used to trigger client side events, see the [documentation](/headers/x-kt-trigger) for examples.
* `X-KT-Push` - can be used to push a new URL into the browsers address bar
* `X-HX-Trigger` - can be used to trigger client side events, see the [documentation](/headers/x-hx-trigger) for examples.
* `X-HX-Push` - can be used to push a new URL into the browsers address bar
### Request Order of Operations
The order of operations in a kutty request are:
The order of operations in a htmx request are:
* The element is triggered and begins a request
* Values are gathered for the request
* The `kutty-request` class is applied to the appropriate elements
* The `htmx-request` class is applied to the appropriate elements
* The request is then issued asynchronously via AJAX
* Upon getting a response the target element is marked with the `kutty-swapping` class
* An optional swap delay is applied (see the [kt-swap-delay](/attributes/kt-swap-delay) attribute)
* Upon getting a response the target element is marked with the `htmx-swapping` class
* An optional swap delay is applied (see the [hx-swap-delay](/attributes/hx-swap-delay) attribute)
* The actual content swap is done
* the `kutty-swapping` class is removed from the target
* the `kutty-settling` class is applied to the target
* the `htmx-swapping` class is removed from the target
* the `htmx-settling` class is applied to the target
* A settle delay is done (default: 100ms)
* The DOM is settled
* the `kutty-settling` class is removed from the target
* the `htmx-settling` class is removed from the target
You can use the `kutty-swapping` and `kutty-settling` classes to create
You can use the `htmx-swapping` and `htmx-settling` classes to create
[CSS transitions](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions) between pages.
## Miscellaneous Attributes
In addition to the core AJAX functionality, kutty also has a few other tricks up its sleeve that help you build
In addition to the core AJAX functionality, htmx also has a few other tricks up its sleeve that help you build
nice interfaces without javascript.
### Class Swapping
Kutty supports an attribute, [kt-classes](/attributes/kt-classes) that allows you to add, remove and toggle classes after
Htmx supports an attribute, [hx-classes](/attributes/hx-classes) that allows you to add, remove and toggle classes after
a delay. This can be used to create CSS transition effects.
Here are some examples:
```html
<!-- adds the class "foo" after 100ms -->
<div kt-classes="add foo"/>
<div hx-classes="add foo"/>
<!-- removes the class "bar" after 1s -->
<div kt-classes="remove bar:1s"/>
<div hx-classes="remove bar:1s"/>
<!-- removes the class "bar" after 1s
then adds the class "foo" 1s after that -->
<div kt-classes="remove bar:1s, add foo:1s"/>
<div hx-classes="remove bar:1s, add foo:1s"/>
<!-- removes the class "bar" and adds
class "foo" after 1s -->
<div kt-classes="remove bar:1s & add foo:1s"/>
<div hx-classes="remove bar:1s & add foo:1s"/>
<!-- toggles the class "foo" every 1s -->
<div kt-classes="toggle foo:1s"/>
<div hx-classes="toggle foo:1s"/>
```
Full documentation is available [on the documentation page.](/attributes/kt-classes)
Full documentation is available [on the documentation page.](/attributes/hx-classes)
### Boosting
Kutty supports "boosting" regular HTML anchors and forms with the [kt-boost](/attributes/kt-boost) attribute. This
Htmx supports "boosting" regular HTML anchors and forms with the [hx-boost](/attributes/hx-boost) attribute. This
attribute will convert all anchor tags and forms into AJAX requests that, by default, target the body of the page.
This functionality is somewhat similar to [Turbolinks](https://github.com/turbolinks/turbolinks).
## <a name="events"></a> [Events & Logging](#events)
Kutty has an extensive events mechanism, which doubles as the logging system.
Htmx has an extensive events mechanism, which doubles as the logging system.
If you want to register for a given kutty event you can use the following javascript:
If you want to register for a given htmx event you can use the following javascript:
```javascript
kutty.on("load.kutty", function(evt) {
htmx.on("load.htmx", function(evt) {
myJavascriptLib.init(evt.details.elt);
});
```
This event is fired every time an element is loaded into the DOM by kutty, and is effectively the load event. In
This event is fired every time an element is loaded into the DOM by htmx, and is effectively the load event. In
fact this is so common, you can use the helper function:
```javascript
kutty.onLoad(function(target) {
htmx.onLoad(function(target) {
myJavascriptLib.init(target);
});
```
@ -504,46 +504,46 @@ The full set of events can be seen [on the reference page](/reference#events).
### Logging
If you set a logger at `kutty.logger`, every event will be logged. This can be very useful for troubleshooting:
If you set a logger at `htmx.logger`, every event will be logged. This can be very useful for troubleshooting:
```javascript
kutty.logger = function(elt, event, data) {
htmx.logger = function(elt, event, data) {
if(console) {
console.log(event, elt, data);
}
}
```
Kutty can also send errors to a URL that is specified with the [kt-error-url](/attributes/kt-error-url) attributes. This can be useful for debugging client-side issues.
Htmx can also send errors to a URL that is specified with the [hx-error-url](/attributes/hx-error-url) attributes. This can be useful for debugging client-side issues.
Kutty includes a helper method:
Htmx includes a helper method:
```javascript
kutty.logAll();
htmx.logAll();
```
if you want to log everything while developing.
## <a name="config"></a>[Configuring kutty](#config)
## <a name="config"></a>[Configuring htmx](#config)
Kutty allows you to configure a few defaults:
Htmx allows you to configure a few defaults:
* `kutty.config.historyEnabled` - defaults to `true`, really only useful for testing
* `kutty.config.historyCacheSize` - defaults to 10
* `kutty.config.defaultSwapStyle` - defaults to `innerHTML`
* `kutty.config.defaultSwapDelay` - defaults to 0
* `kutty.config.defaultSettleDelay` - defaults to 100
* `kutty.config.includeIndicatorStyles` - defaults to `true` (determines if the `kutty-indicator` default styles are loaded, must be set in a `meta` tag before the kutty js is included)
* `htmx.config.historyEnabled` - defaults to `true`, really only useful for testing
* `htmx.config.historyCacheSize` - defaults to 10
* `htmx.config.defaultSwapStyle` - defaults to `innerHTML`
* `htmx.config.defaultSwapDelay` - defaults to 0
* `htmx.config.defaultSettleDelay` - defaults to 100
* `htmx.config.includeIndicatorStyles` - defaults to `true` (determines if the `htmx-indicator` default styles are loaded, must be set in a `meta` tag before the htmx js is included)
You can set them directly in javascript, or you can use a `meta` tag:
```html
<meta name="kutty-config" content='{"defaultSwapStyle":"outerHTML"}'>
<meta name="htmx-config" content='{"defaultSwapStyle":"outerHTML"}'>
```
### Conclusion
And that's it! Have fun with kutty: you can accomplish [quite a bit](/examples) without a lot of code.
And that's it! Have fun with htmx: you can accomplish [quite a bit](/examples) without a lot of code.
</div>
</div>

View File

@ -1,14 +1,14 @@
---
layout: layout.njk
title: </> kutty - high power tools for html
title: </> htmx - high power tools for html
---
## Events
Kutty provides an extensive events system that can be used to modify and enhance behavior. Events
Htmx provides an extensive events system that can be used to modify and enhance behavior. Events
are listed below.
### <a name="afterOnLoad.kutty"></a> Event - [`afterOnLoad.kutty`](#afterOnLoad.kutty)
### <a name="afterOnLoad.htmx"></a> Event - [`afterOnLoad.htmx`](#afterOnLoad.htmx)
This event is triggered after an AJAX `onload` has finished. Note that this does not mean that the content
has been swapped or settled yet, only that the request has finished.
@ -19,7 +19,7 @@ has been swapped or settled yet, only that the request has finished.
* `detail.xhr` - the `XMLHttpRequest`
* `detail.target` - the target of the request
### <a name="afterSettle.kutty"></a> Event - [`afterSettle.kutty`](#afterSettle.kutty)
### <a name="afterSettle.htmx"></a> Event - [`afterSettle.htmx`](#afterSettle.htmx)
This event is triggered after the DOM has [settled](/docs#settling).
@ -29,7 +29,7 @@ This event is triggered after the DOM has [settled](/docs#settling).
* `detail.xhr` - the `XMLHttpRequest`
* `detail.target` - the target of the request
### <a name="afterSwap.kutty"></a> Event - [`afterSwap.kutty`](#afterSwap.kutty)
### <a name="afterSwap.htmx"></a> Event - [`afterSwap.htmx`](#afterSwap.htmx)
This event is triggered after new content has been [swapped into the DOM](/docs#swapping).
@ -39,7 +39,7 @@ This event is triggered after new content has been [swapped into the DOM](/docs
* `detail.xhr` - the `XMLHttpRequest`
* `detail.target` - the target of the request
### <a name="beforeOnLoad.kutty"></a> Event - [`beforeOnLoad.kutty`](#beforeOnLoad.kutty)
### <a name="beforeOnLoad.htmx"></a> Event - [`beforeOnLoad.htmx`](#beforeOnLoad.htmx)
This event is triggered before any new content has been [swapped into the DOM](/docs#swapping). If
the event is cancelled, no swap will occur.
@ -50,7 +50,7 @@ the event is cancelled, no swap will occur.
* `detail.xhr` - the `XMLHttpRequest`
* `detail.target` - the target of the request
### <a name="beforeRequest.kutty"></a> Event - [`beforeRequest.kutty`](#beforeRequest.kutty)
### <a name="beforeRequest.htmx"></a> Event - [`beforeRequest.htmx`](#beforeRequest.htmx)
This event is triggered before an AJAX request is issued. If the event is cancelled, no request will occur.
@ -60,7 +60,7 @@ This event is triggered before an AJAX request is issued. If the event is cance
* `detail.xhr` - the `XMLHttpRequest`
* `detail.target` - the target of the request
### <a name="historyCacheMiss.kutty"></a> Event - [`historyCacheMiss.kutty`](#historyCacheMiss.kutty)
### <a name="historyCacheMiss.htmx"></a> Event - [`historyCacheMiss.htmx`](#historyCacheMiss.htmx)
This event is triggered when a cache miss occurs when restoring history
@ -69,7 +69,7 @@ This event is triggered when a cache miss occurs when restoring history
* `detail.xhr` - the `XMLHttpRequest` that will retrieve the remote content for restoration
* `detail.path` - the path and query of the page being restored
### <a name="historyCacheMissLoad.kutty"></a> Event - [`historyCacheMissLoad.kutty`](#historyCacheMissLoad.kutty)
### <a name="historyCacheMissLoad.htmx"></a> Event - [`historyCacheMissLoad.htmx`](#historyCacheMissLoad.htmx)
This event is triggered when a cache miss occurs and a response has been retrieved succesfully from the server
for the content to restore
@ -79,7 +79,7 @@ for the content to restore
* `detail.xhr` - the `XMLHttpRequest`
* `detail.path` - the path and query of the page being restored
### <a name="historyCacheMissError.kutty"></a> Event - [`historyCacheMissError.kutty`](#historyCacheMissError.kutty)
### <a name="historyCacheMissError.htmx"></a> Event - [`historyCacheMissError.htmx`](#historyCacheMissError.htmx)
This event is triggered when a cache miss occurs and a response has been retrieved from the server
for the content to restore, but the response is an error (e.g. `404`)
@ -89,26 +89,26 @@ for the content to restore, but the response is an error (e.g. `404`)
* `detail.xhr` - the `XMLHttpRequest`
* `detail.path` - the path and query of the page being restored
### <a name="historyRestore.kutty"></a> Event - [`historyRestore.kutty`](#historyRestore.kutty)
### <a name="historyRestore.htmx"></a> Event - [`historyRestore.htmx`](#historyRestore.htmx)
This event is triggered when kutty handles a history restoration action
This event is triggered when htmx handles a history restoration action
##### Details
* `detail.path` - the path and query of the page being restored
### <a name="beforeHistorySave.kutty"></a> Event - [`beforeHistorySave.kutty`](#beforeHistorySave.kutty)
### <a name="beforeHistorySave.htmx"></a> Event - [`beforeHistorySave.htmx`](#beforeHistorySave.htmx)
This event is triggered when kutty handles a history restoration action
This event is triggered when htmx handles a history restoration action
##### Details
* `detail.path` - the path and query of the page being restored
* `detail.historyElt` - the history element being restored into
### <a name="initSSE.kutty"></a> Event - [`initSSE.kutty`](#initSSE.kutty)
### <a name="initSSE.htmx"></a> Event - [`initSSE.htmx`](#initSSE.htmx)
This event is triggered when kutty initializes a new SSE source. It can be used
This event is triggered when htmx initializes a new SSE source. It can be used
to [configure the source](https://developer.mozilla.org/en-US/docs/Web/API/EventSource/EventSource).
Note that by default `withCredentials` will be set to `true` in the configuration.
@ -117,15 +117,15 @@ Note that by default `withCredentials` will be set to `true` in the configuratio
* `detail.config` - the config that will be passed to the `EventSource` contstructor
### <a name="load.kutty"></a> Event - [`load.kutty`](#load.kutty)
### <a name="load.htmx"></a> Event - [`load.htmx`](#load.htmx)
This event is triggered when a new node is loaded into the DOM by kutty.
This event is triggered when a new node is loaded into the DOM by htmx.
##### Details
* `detail.elt` - the newly added element
### <a name="noSSESourceError.kutty"></a> Event - [`noSSESourceError.kutty`](#noSSESourceError.kutty)
### <a name="noSSESourceError.htmx"></a> Event - [`noSSESourceError.htmx`](#noSSESourceError.htmx)
This event is triggered when an element refers to a SSE event in its trigger, but no parent SSE source has been defined
@ -133,7 +133,7 @@ This event is triggered when an element refers to a SSE event in its trigger, bu
* `detail.elt` - the element with the bad SSE trigger
### <a name="onLoadError.kutty"></a> Event - [`onLoadError.kutty`](#onLoadError.kutty)
### <a name="onLoadError.htmx"></a> Event - [`onLoadError.htmx`](#onLoadError.htmx)
This event is triggered when an error occurs during the `load` handling of an AJAX call
@ -144,7 +144,7 @@ This event is triggered when an error occurs during the `load` handling of an AJ
* `detail.target` - the target of the request
* `detail.exception` - the exception that occurred
### <a name="oobErrorNoTarget.kutty"></a> Event - [`oobErrorNoTarget.kutty`](#oobErrorNoTarget.kutty)
### <a name="oobErrorNoTarget.htmx"></a> Event - [`oobErrorNoTarget.htmx`](#oobErrorNoTarget.htmx)
This event is triggered when an [out of band swap](/docs##oob_swaps) does not have a corresponding element
in the DOM to switch with.
@ -153,9 +153,9 @@ in the DOM to switch with.
* `detail.content` - the element with the bad oob `id`
### <a name="prompt.kutty"></a> Event - [`prompt.kutty`](#prompt.kutty)
### <a name="prompt.htmx"></a> Event - [`prompt.htmx`](#prompt.htmx)
This event is triggered after a prompt has been shown to the user with the [`kt-prompt`](/attributes/kt-prompt)
This event is triggered after a prompt has been shown to the user with the [`hx-prompt`](/attributes/hx-prompt)
attribute. If this event is cancelled, the AJAX request will not occur.
##### Details
@ -164,7 +164,7 @@ attribute. If this event is cancelled, the AJAX request will not occur.
* `detail.target` - the target of the request
* `detail.prompt` - the user response to the prompt
### <a name="responseError.kutty"></a> Event - [`responseError.kutty`](#responseError.kutty)
### <a name="responseError.htmx"></a> Event - [`responseError.htmx`](#responseError.htmx)
This event is triggered when an HTTP error response occurs
@ -174,7 +174,7 @@ This event is triggered when an HTTP error response occurs
* `detail.elt` - the element that triggered the request
* `detail.target` - the target of the request
### <a name="sendError.kutty"></a> Event - [`sendError.kutty`](#sendError.kutty)
### <a name="sendError.htmx"></a> Event - [`sendError.htmx`](#sendError.htmx)
This event is triggered when a network error prevents an HTTP request from occurring
@ -184,7 +184,7 @@ This event is triggered when a network error prevents an HTTP request from occur
* `detail.elt` - the element that triggered the request
* `detail.target` - the target of the request
### <a name="sseError.kutty"></a> Event - [`sseError.kutty`](#sseError.kutty)
### <a name="sseError.htmx"></a> Event - [`sseError.htmx`](#sseError.htmx)
This event is triggered when an error occurs with a SSE source
@ -194,7 +194,7 @@ This event is triggered when an error occurs with a SSE source
* `detail.error` - the error
* `detail.source` - the SSE source
### <a name="swapError.kutty"></a> Event - [`swapError.kutty`](#swapError.kutty)
### <a name="swapError.htmx"></a> Event - [`swapError.htmx`](#swapError.htmx)
This event is triggered when an error occurs during the swap phase
@ -204,13 +204,13 @@ This event is triggered when an error occurs during the swap phase
* `detail.elt` - the element that triggered the request
* `detail.target` - the target of the request
### <a name="configRequest.kutty"></a> Event - [`configRequest.kutty`](#configRequest.kutty)
### <a name="configRequest.htmx"></a> Event - [`configRequest.htmx`](#configRequest.htmx)
This event is triggered after kutty has collected parameters for inclusion in the request. It can be
used to include or update the parameters that kutty will send:
This event is triggered after htmx has collected parameters for inclusion in the request. It can be
used to include or update the parameters that htmx will send:
```javascript
document.body.addEventListener('configRequest.kutty', function(evt) {
document.body.addEventListener('configRequest.htmx', function(evt) {
evt.detail.parameters['auth_token'] = getAuthToken(); // add a new parameter into the mix
});
```
@ -227,9 +227,9 @@ than a single value.
* `detail.target` - the target of the request
* `detail.verb` - the HTTP verb in use
### <a name="targetError.kutty"></a> Event - [`targetError.kutty`](#targetError.kutty)
### <a name="targetError.htmx"></a> Event - [`targetError.htmx`](#targetError.htmx)
This event is triggered when a bad selector is used for a [`kt-target`](/attributes/kt-target) attribute (e.g. an
This event is triggered when a bad selector is used for a [`hx-target`](/attributes/hx-target) attribute (e.g. an
element id without a preceding `#`)
##### Details

View File

@ -1,11 +1,11 @@
---
layout: layout.njk
title: </> kutty - UX Patterns
title: </> htmx - UX Patterns
---
## UI Examples
Below are a set of UX patterns implemented in kutty with minimal HTML and styling.
Below are a set of UX patterns implemented in htmx with minimal HTML and styling.
You can copy and paste them and then adjust them for your needs.

View File

@ -11,16 +11,16 @@ We start with a search input and an empty table:
```html
<h3>
Search Contacts
<span class="kutty-indicator">
<span class="htmx-indicator">
<img src="/img/bars.svg"/> Searching...
</span>
</h3>
<input class="form-control" type="text"
name="search" placeholder="Begin Typing To Search Users..."
kt-post="/search"
kt-trigger="keyup changed delay:500ms"
kt-target="#search-results"
kt-indicator=".kutty-indicator">
hx-post="/search"
hx-trigger="keyup changed delay:500ms"
hx-target="#search-results"
hx-indicator=".htmx-indicator">
<table class="table">
<thead>
@ -41,7 +41,7 @@ We add the `delay:500ms` modifier to the trigger to delay sending the query unti
we add the `changed` modifier to the trigger to ensure we don't send new queries when the user doesn't change the
value of the input (e.g. they hit an arrow key).
Finally, we show an indicator when the search is in flight with the `kt-indicator` attribute.
Finally, we show an indicator when the search is in flight with the `hx-indicator` attribute.
{% include demo_ui.html.liquid %}
@ -67,17 +67,17 @@ Finally, we show an indicator when the search is in flight with the `kt-indicato
function searchUI() {
return ` <h3>
Search Contacts
<span class="kutty-indicator">
<span class="htmx-indicator">
<img src="/img/bars.svg"/> Searching...
</span>
</h3>
<input class="form-control" type="text"
name="search" placeholder="Begin Typing To Search Users..."
kt-post="/search"
kt-trigger="keyup changed delay:500ms"
kt-target="#search-results"
kt-indicator=".kutty-indicator">
hx-post="/search"
hx-trigger="keyup changed delay:500ms"
hx-target="#search-results"
hx-indicator=".htmx-indicator">
<table class="table">
<thead>

View File

@ -9,9 +9,9 @@ accomplished by putting a form around a table, with checkboxes in the table, and
values in `POST`'s to two different endpoints: `activate` and `deactivate`:
```html
<div kt-include="#checked-contacts" kt-target="#tbody">
<a class="btn" kt-put="/activate">Activate</a>
<a class="btn" kt-put="/deactivate">Deactivate</a>
<div hx-include="#checked-contacts" hx-target="#tbody">
<a class="btn" hx-put="/activate">Activate</a>
<a class="btn" hx-put="/deactivate">Deactivate</a>
</div>
<form id="checked-contacts">
@ -42,10 +42,10 @@ updated rows. It will apply the class `activate` or `deactivate` to rows that h
us to use a bit of CSS to flash a color helping the user see what happened:
```css
.kutty-settling tr.deactivate td {
.htmx-settling tr.deactivate td {
background: lightcoral;
}
.kutty-settling tr.activate td {
.htmx-settling tr.activate td {
background: darkseagreen;
}
tr td {
@ -56,10 +56,10 @@ us to use a bit of CSS to flash a color helping the user see what happened:
You can see a working examle of this code below.
<style scoped="">
.kutty-settling tr.deactivate td {
.htmx-settling tr.deactivate td {
background: lightcoral;
}
.kutty-settling tr.activate td {
.htmx-settling tr.activate td {
background: darkseagreen;
}
tr td {
@ -127,9 +127,9 @@ You can see a working examle of this code below.
// templates
function displayUI(contacts) {
return `<div kt-include="#checked-contacts" kt-target="#tbody">
<a class="btn" kt-put="/activate">Activate</a>
<a class="btn" kt-put="/deactivate">Deactivate</a>
return `<div hx-include="#checked-contacts" hx-target="#tbody">
<a class="btn" hx-put="/activate">Activate</a>
<a class="btn" hx-put="/deactivate">Deactivate</a>
</div>
<form id="checked-contacts">

View File

@ -9,11 +9,11 @@ The click to edit pattern provides a way to offer inline editing of all or part
* This pattern starts with a UI that shows the details of a contact. The div has a button that will get the editing UI for the contact from `/contacts/1/edit`
```html
<div kt-target="this" kt-swap="outerHTML">
<div hx-target="this" hx-swap="outerHTML">
<div><label>First Name</label>: Joe</div>
<div><label>Last Name</label>: Blow</div>
<div><label>Email</label>: joe@blow.com</div>
<button kt-get="/contact/1/edit" class="btn btn-primary">
<button hx-get="/contact/1/edit" class="btn btn-primary">
Click To Edit
</button>
</div>
@ -22,7 +22,7 @@ The click to edit pattern provides a way to offer inline editing of all or part
* This returns a form that can be used to edit the contact
```html
<form kt-put="/contact/1" kt-target="this" kt-swap="outerHTML">
<form hx-put="/contact/1" hx-target="this" hx-swap="outerHTML">
<div>
<label>First Name</label>
<input type="text" name="firstName" value="Joe">
@ -36,7 +36,7 @@ The click to edit pattern provides a way to offer inline editing of all or part
<input type="email" name="email" value="joe@blow.com">
</div>
<button class="btn">Submit</button>
<button class="btn" kt-get="/contact/1">Cancel</button>
<button class="btn" hx-get="/contact/1">Cancel</button>
</form>
```
@ -74,7 +74,7 @@ The click to edit pattern provides a way to offer inline editing of all or part
// templates
function formTemplate(contact) {
return `<form kt-put="/contact/1" kt-target="this" kt-swap="outerHTML">
return `<form hx-put="/contact/1" hx-target="this" hx-swap="outerHTML">
<div>
<label>First Name</label>
<input type="text" name="firstName" value="${contact.firstName}">
@ -88,16 +88,16 @@ return `<form kt-put="/contact/1" kt-target="this" kt-swap="outerHTML">
<input type="email" name="email" value="${contact.email}">
</div>
<button class="btn">Submit</button>
<button class="btn" kt-get="/contact/1">Cancel</button>
<button class="btn" hx-get="/contact/1">Cancel</button>
</form>`
}
function displayTemplate(contact) {
return `<div kt-target="this" kt-swap="outerHTML">
return `<div hx-target="this" hx-swap="outerHTML">
<div><label>First Name</label>: ${contact.firstName}</div>
<div><label>Last Name</label>: ${contact.lastName}</div>
<div><label>Email</label>: ${contact.email}</div>
<button kt-get="/contact/1/edit" class="btn btn-primary">
<button hx-get="/contact/1/edit" class="btn btn-primary">
Click To Edit
</button>
</div>`;

View File

@ -10,10 +10,10 @@ the final row:
```html
<tr id="replaceMe">
<td colspan="3">
<button class='btn' kt-get="/contacts/?page=2"
kt-target="#replaceMe"
kt-swap="outerHTML">
Load More Agents... <img class="kutty-indicator" src="/img/bars.svg">
<button class='btn' hx-get="/contacts/?page=2"
hx-target="#replaceMe"
hx-swap="outerHTML">
Load More Agents... <img class="htmx-indicator" src="/img/bars.svg">
</button>
</td>
</tr>
@ -83,10 +83,10 @@ results (which will contain a button to load the *next* page of results). And s
return `<tr id="replaceMe">
<td colspan="3">
<center>
<button class='btn' kt-get="/contacts/?page=${page + 1}"
kt-target="#replaceMe"
kt-swap="outerHTML">
Load More Agents... <img class="kutty-indicator" src="/img/bars.svg">
<button class='btn' hx-get="/contacts/?page=${page + 1}"
hx-target="#replaceMe"
hx-swap="outerHTML">
Load More Agents... <img class="htmx-indicator" src="/img/bars.svg">
</button>
</center>
</td>

View File

@ -2,16 +2,16 @@
layout: demo_layout.njk
---
## Kutty Pattern: Infinite scroll
## Htmx Pattern: Infinite scroll
The infinite scroll pattern provides a way to load content dynamically on user scrolling action.
Let's focus on the final row:
```html
<tr kt-get="/contacts/?page=2"
kt-trigger="revealed"
kt-swap="afterend">
<tr hx-get="/contacts/?page=2"
hx-trigger="revealed"
hx-swap="afterend">
<td>Agent Smith</td>
<td>void29@null.org</td>
<td>55F49448C0</td>
@ -65,9 +65,9 @@ The last element of the results will itself contain the listener to load the *ne
// templates
function tableTemplate(contacts) {
return `<table kt-indicator=".kutty-indicator"><thead><tr><th>Name</th><th>Email</th><th>ID</th></tr></thead><tbody>
return `<table hx-indicator=".htmx-indicator"><thead><tr><th>Name</th><th>Email</th><th>ID</th></tr></thead><tbody>
${rowsTemplate(1, contacts)}
</tbody></table><center><img class="kutty-indicator" width="60" src="/img/bars.svg"></center>`
</tbody></table><center><img class="htmx-indicator" width="60" src="/img/bars.svg"></center>`
}
function rowsTemplate(page, contacts) {
@ -78,7 +78,7 @@ The last element of the results will itself contain the listener to load the *ne
var c = contacts[i];
if (i == (contacts.length - 1)) {
trigger_attributes = ` kt-get="/contacts/?page=${page + 1}" kt-trigger="revealed" kt-swap="afterend"`
trigger_attributes = ` hx-get="/contacts/?page=${page + 1}" hx-trigger="revealed" hx-swap="afterend"`
}
txt += "<tr" + trigger_attributes +"><td>" + c.name + "</td><td>" + c.email + "</td><td>" + c.id + "</td></tr>\n";

View File

@ -12,11 +12,11 @@ We start with this form:
```html
<h3>Signup Form</h3>
<form kt-post="/contact">
<div kt-target="this" kt-swap="outerHTML">
<form hx-post="/contact">
<div hx-target="this" hx-swap="outerHTML">
<label>Email Address</label>
<input name="email" kt-post="/contact/email" kt-indicator="#ind">
<img id="ind" src="/img/bars.svg" class="kutty-indicator"/>
<input name="email" hx-post="/contact/email" hx-indicator="#ind">
<img id="ind" src="/img/bars.svg" class="htmx-indicator"/>
</div>
<div class="form-group">
<label>First Name</label>
@ -37,10 +37,10 @@ It also specifies an indicator for the request.
When a request occurs, it will return a partial to replace the outer div. It might look like this:
```html
<div kt-target="this" kt-swap="outerHTML" class="error">
<div hx-target="this" hx-swap="outerHTML" class="error">
<label>Email Address</label>
<input name="email" kt-post="/contact/email" kt-indicator="#ind" value="test@foo.com">
<img id="ind" src="/img/bars.svg" class="kutty-indicator"/>
<input name="email" hx-post="/contact/email" hx-indicator="#ind" value="test@foo.com">
<img id="ind" src="/img/bars.svg" class="htmx-indicator"/>
<div class='error-message'>That email is already taken. Please enter another email.</div>
</div>
```
@ -108,10 +108,10 @@ Below is a working demo of this example. The only email that will be accepted i
// templates
function formTemplate(page) {
return `<h3>Signup Form</h3><form ic-post-to="/contact">
<div kt-target="this" kt-swap="outerHTML">
<div hx-target="this" hx-swap="outerHTML">
<label>Email Address</label>
<input name="email" kt-get="/contact/email" kt-indicator="#ind">
<img id="ind" src="/img/bars.svg" class="kutty-indicator"/>
<input name="email" hx-get="/contact/email" hx-indicator="#ind">
<img id="ind" src="/img/bars.svg" class="htmx-indicator"/>
</div>
<div class="form-group">
<label>First Name</label>
@ -126,10 +126,10 @@ Below is a working demo of this example. The only email that will be accepted i
}
function emailInputTemplate(val, errorMsg) {
return `<div kt-target="this" kt-swap="outerHTML" class="${errorMsg ? "error" : "valid"}">
return `<div hx-target="this" hx-swap="outerHTML" class="${errorMsg ? "error" : "valid"}">
<label>Email Address</label>
<input name="email" kt-get="/contact/email" kt-indicator="#ind" value="${val}">
<img id="ind" src="/img/bars.svg" class="kutty-indicator"/>
<input name="email" hx-get="/contact/email" hx-indicator="#ind" value="${val}">
<img id="ind" src="/img/bars.svg" class="htmx-indicator"/>
${errorMsg ? ("<div class='error-message'>" + errorMsg + "</div>") : ""}
</div>`;
}

View File

@ -8,8 +8,8 @@ This example shows how to lazily load an element on a page. We start with an in
state that looks like this:
```html
<div kt-get="/graph" kt-trigger="load">
<img class="kutty-indicator" width="150" src="/img/bars.svg"/>
<div hx-get="/graph" hx-trigger="load">
<img class="htmx-indicator" width="150" src="/img/bars.svg"/>
</div>
```
@ -17,7 +17,7 @@ Which shows a progress indicator as we are loading the graph. The graph is then
loaded and faded gently into view via a settling CSS transition:
```css
.kutty-settling img {
.htmx-settling img {
opacity: 0;
}
img {
@ -26,7 +26,7 @@ img {
```
<style>
.kutty-settling img {
.htmx-settling img {
opacity: 0;
}
img {
@ -54,8 +54,8 @@ img {
// templates
function lazyTemplate(page) {
return `<div kt-get="/graph" kt-trigger="load">
<img class="kutty-indicator" width="120" src="/img/bars.svg"/>
return `<div hx-get="/graph" hx-trigger="load">
<img class="htmx-indicator" width="120" src="/img/bars.svg"/>
</div>`;
}
</script>

View File

@ -9,9 +9,9 @@ This example shows how to implement a smoothly scrolling progress bar.
We start with an intial state with a button that issues a `POST` to `/start` to begin the job:
```html
<div kt-target="this" kt-swap="outerHTML">
<div hx-target="this" hx-swap="outerHTML">
<h3>Start Progress</h3>
<button class="btn" kt-post="/start">
<button class="btn" hx-post="/start">
Start Job
</button>
</div>
@ -20,10 +20,10 @@ We start with an intial state with a button that issues a `POST` to `/start` to
This div is then replaced with a new div that reloads itself every 600ms:
```html
<div kt-target="this"
kt-get="/job"
kt-trigger="load delay:600ms"
kt-swap="outerHTML">
<div hx-target="this"
hx-get="/job"
hx-trigger="load delay:600ms"
hx-swap="outerHTML">
<h3>Running</h3>
<div class="progress">
<div id="pb" class="progress-bar" style="width:0%">
@ -31,23 +31,23 @@ This div is then replaced with a new div that reloads itself every 600ms:
</div>
```
This HTML is rerendered every 600 milliseconds, with the "width" style attribute on the progress bar being updated.
Because there is an id on the progress bar div, kutty will smoothly transition between requests by settling the
Because there is an id on the progress bar div, htmx will smoothly transition between requests by settling the
style attribute into its new value. This, when coupled with CSS transitions, make the visual transition continuous
rather than jumpy.
Finally, when the process is complete, a restart button is added to the UI:
```html
<div kt-target="this"
kt-get="/job"
kt-trigger="none"
kt-swap="outerHTML">
<div hx-target="this"
hx-get="/job"
hx-trigger="none"
hx-swap="outerHTML">
<h3>Complete</h3>
<div class="progress">
<div id="pb" class="progress-bar" style="width:100%">
</div>
</div>
<button id="restart-btn" class="btn" kt-post="/start" kt-classes="add show:600ms">
<button id="restart-btn" class="btn" hx-post="/start" hx-classes="add show:600ms">
Restart Job
</button>
```
@ -109,19 +109,19 @@ Finally, when the process is complete, a restart button is added to the UI:
// templates
function startButton(message) {
return `<div kt-target="this" kt-swap="outerHTML">
return `<div hx-target="this" hx-swap="outerHTML">
<h3>${message}</h3>
<button class="btn" kt-post="/start">
<button class="btn" hx-post="/start">
Start Job
</button>
</div>`;
}
function jobStatusTemplate(job) {
return `<div kt-target="this"
kt-get="/job"
kt-trigger="${job.complete ? 'none' : 'load delay:600ms'}"
kt-swap="outerHTML">
return `<div hx-target="this"
hx-get="/job"
hx-trigger="${job.complete ? 'none' : 'load delay:600ms'}"
hx-swap="outerHTML">
<h3>${job.complete ? "Complete" : "Running"}</h3>
<div class="progress">
<div id="pb" class="progress-bar" style="width:${job.percentComplete}%">
@ -132,7 +132,7 @@ ${restartButton(job)}`;
function restartButton(job) {
if(job.complete){
return `<button id="restart-btn" class="btn" kt-post="/start" kt-classes="add show:600ms">
return `<button id="restart-btn" class="btn" hx-post="/start" hx-classes="add show:600ms">
Restart Job
</button>`
} else {

View File

@ -14,7 +14,7 @@ Here is the code:
```html
<div>
<label >Make</label>
<select name="make" kt-get="/models" kt-target="#models" kt-indicator=".kutty-indicator">
<select name="make" hx-get="/models" hx-target="#models" hx-indicator=".htmx-indicator">
<option value="audi">Audi</option>
<option value="toyota">Toyota</option>
<option value="bmw">BMW</option>
@ -63,7 +63,7 @@ And they become available in the `model` select.
<form>
<div>
<label >Make</label>
<select name="make" kt-get="/models" kt-target="#models" kt-indicator=".kutty-indicator">
<select name="make" hx-get="/models" hx-target="#models" hx-indicator=".htmx-indicator">
<option value="audi">Audi</option>
<option value="toyota">Toyota</option>
<option value="bmw">BMW</option>
@ -76,7 +76,7 @@ And they become available in the `model` select.
<option value="a3">A3</option>
<option value="a6">A6</option>
</select>
<img class="kutty-indicator" width="20" src="/img/bars.svg">
<img class="htmx-indicator" width="20" src="/img/bars.svg">
</div>
</form>`;
}

View File

@ -1,16 +1,16 @@
---
layout: layout.njk
title: </> kutty - X-KT-Trigger
title: </> htmx - X-HX-Trigger
---
## `X-KT-Trigger` Response Header
## `X-HX-Trigger` Response Header
The `X-KT-Trigger` response header can be used to trigger client side actions from a response to kutty. You can
The `X-HX-Trigger` response header can be used to trigger client side actions from a response to htmx. You can
trigger a single event or as many uniquely named events as you would like.
To trigger a single event with no additional details you can simply send the header like so:
`X-KT-Trigger: myEvent`
`X-HX-Trigger: myEvent`
This will trigger `myEvent` on the triggering element and will bubble up to the body. As an example you could
listen for this event like this:
@ -23,7 +23,7 @@ document.body.addEventListener("myEvent", function(evt){
If you want to pass details along with the event, you can move to JSON for the value of the trigger:
`X-KT-Trigger: {"showMessage":"Here Is A Message"}`
`X-HX-Trigger: {"showMessage":"Here Is A Message"}`
To handle this event you would write the following code:
@ -36,7 +36,7 @@ document.body.addEventListener("showMessage", function(evt){
Note that the value of the message was put into the `detail.value` slot. If you wish to pass multiple pieces of data
you can use a nested JSON object on the right hand side of the JSON object:
`X-KT-Trigger: {"showMessage":{"level" : "info", "message" : "Here Is A Message"}}`
`X-HX-Trigger: {"showMessage":{"level" : "info", "message" : "Here Is A Message"}}`
And handle this event like so:
@ -53,6 +53,6 @@ Each property of the JSON object on the right hand side will be copied onto the
Finally, if you wish to invoke multiple events, you can simply add additional properties to the top level JSON
object:
`X-KT-Trigger: {"event1":"A message", "event2":"Another message"}`
`X-HX-Trigger: {"event1":"A message", "event2":"Another message"}`
Using events gives you a lot of flexibility to add functionality to normal kutty responses.
Using events gives you a lot of flexibility to add functionality to normal htmx responses.

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

View File

@ -1,37 +1,37 @@
---
layout: layout.njk
title: </> kutty - high power tools for html
title: </> htmx - high power tools for html
---
<div class="dark-hero full-width" kt-classes="add appear">
<span class="logo dark">&lt;<a>/</a>&gt; <span class="no-mobile">k<a>u</a>tty</span></span>
<div class="dark-hero full-width" hx-classes="add appear">
<span class="logo dark">&lt;<a>/</a>&gt; <span class="no-mobile">htm<a>x</a></span></span>
<sub class="no-mobile"><i>high power tools for HTML</i></sub>
</div>
## Introduction
Kutty is a set of extensions (attributes, request headers, etc.) that help you build
Htmx is a set of extensions (attributes, request headers, etc.) that help you build
[modern UI](/examples) with the [simplicity](https://en.wikipedia.org/wiki/HATEOAS) and
[power](https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm) of HTML.
Kutty is small ([~6k min.gz'd](https://unpkg.com/kutty.org/dist/)), IE11 compatible, [dependency-free](https://github.com/bigskysoftware/kutty/blob/master/package.json)
Htmx is small ([~6k min.gz'd](https://unpkg.com/htmx.org/dist/)), IE11 compatible, [dependency-free](https://github.com/bigskysoftware/htmx/blob/master/package.json)
& you can try it out quickly, without a huge rewrite.
## Quick Start
```html
<!-- Load from unpkg -->
<script src="https://unpkg.com/kutty.org@0.0.2"></script>
<script src="https://unpkg.com/htmx.org@0.0.2"></script>
<!-- have a button POST a click via AJAX -->
<button kt-post="/clicked" kt-swap="outerHTML">
<button hx-post="/clicked" hx-swap="outerHTML">
Click Me
</button>
```
The `kt-post` and `kt-swap` attributes tell kutty:
The `hx-post` and `hx-swap` attributes tell htmx:
> "When a user clicks on this button, issue an AJAX request to /clicked, and replace the entire button with the response"
Kutty is based on [intercooler.js](http://intercoolerjs.org) and is the successor to that project.
Htmx is based on [intercooler.js](http://intercoolerjs.org) and is the successor to that project.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,38 @@
---
layout: layout.njk
tags: post
title: htmx 0.0.2 has been released!
---
## htmx 0.0.3 Release
I'm pleased to announce the [0.0.2 release](https://unpkg.com/browse/htmx.org@0.0.3/) of kutty, the successor
to [intercooler.js](http://intercoolerjs.org)!
#### Why not kutty 0.0.2?
One of the reasons you put a `0.0.1` release out there is to see what happens. And one of the things that
happened was that multiple people made comments on how the word "kutty" meant different things in different languages, including
"small", "child" and a very unfortunate meaning in dutch slang. I had originally
[called the project `htmx`](https://github.com/bigskysoftware/kutty/commit/b003ccadf855fe49a40ca0b86ca3c9e16448d33c#diff-b9cfc7f2cdf78a7f4b91a753d10865a2)
(html extensions) and went back and forth between the two names for a bit.
It seems like, upon contact with reality, `htmx` is a better long term name for the project. It's also
a lot easier to search twitter & reddit for that term.
It's a simple fix for anyone who actually used `0.0.1`:
* attributes go from `kt-` to `hx-` (their original prefix)
* request headers go from `X-KT-` to `X-HX-`
* `kutty` goes to `htmx` for event names, etc.
#### Changes
OK, so besides the big re-rename, what changed?
* A bug fix for the `hx-prompt` attribute
* A bug fix for multiple `hx-swap-oob` attributes
* Moved the default CSS indicator injection into its own sheet to avoid breaking
* Added the `htmx.config.includeIndicatorStyles` configuration option so people can opt out of injecting the indicator CSS
Cheers!

View File

@ -1,42 +1,42 @@
---
layout: layout.njk
title: </> kutty - Attributes
title: </> htmx - Attributes
---
## <a name="attributes"></a> [Attribute Reference](#attributes)
| Attribute | Description |
|-----------|-------------|
| [`kt-boost`](/attributes/kt-boost) | progressively enhances anchors and forms to use AJAX requests
| [`kt-classes`](/attributes/kt-classes) | timed modification of classes on an element
| [`kt-confirm`](/attributes/kt-confirm) | shows a confim() dialog before issuing a request
| [`kt-delete`](/attributes/kt-delete) | issues a `DELETE` to the specified URL
| [`kt-error-url`](/attributes/kt-error-url) | a URL to send client-side errors to
| [`kt-get`](/attributes/kt-get) | issues a `GET` to the specified URL
| [`kt-history-elt`](/attributes/kt-history-elt) | the element to snapshot and restore during history navigation
| [`kt-include`](/attributes/kt-include) | includes additional data in AJAX requests
| [`kt-indicator`](/attributes/kt-indicator) | the element to put the `kutty-request` class on during the AJAX request
| [`kt-params`](/attributes/kt-params) | filters the parameters that will be submitted with a request
| [`kt-patch`](/attributes/kt-patch) | issues a `PATCH` to the specified URL
| [`kt-post`](/attributes/kt-post) | issues a `POST` to the specified URL
| [`kt-prompt`](/attributes/kt-prompt) | shows a prompt before submitting a request
| [`kt-push-url`](/attributes/kt-push-url) | pushes the URL into the location bar, creating a new history entry
| [`kt-put`](/attributes/kt-put) | issues a `PUT` to the specified URL
| [`kt-select`](/attributes/kt-select) | selects a subset of the server response to process
| [`kt-sse-src`](/attributes/kt-sse-src) | establishes an [SSE](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events) source for events
| [`kt-swap-oob`](/attributes/kt-swap-oob) | marks content in a response as being "Out of Band", i.e. swapped somewhere other than the target
| [`kt-swap`](/attributes/kt-swap) | controls how the response content is swapped into the DOM (e.g. 'outerHTML' or 'beforeEnd')
| [`kt-target`](/attributes/kt-target) | specifies the target element to be swapped
| [`kt-trigger`](/attributes/kt-trigger) | specifies the event that triggers the request
| [`hx-boost`](/attributes/hx-boost) | progressively enhances anchors and forms to use AJAX requests
| [`hx-classes`](/attributes/hx-classes) | timed modification of classes on an element
| [`hx-confirm`](/attributes/hx-confirm) | shows a confim() dialog before issuing a request
| [`hx-delete`](/attributes/hx-delete) | issues a `DELETE` to the specified URL
| [`hx-error-url`](/attributes/hx-error-url) | a URL to send client-side errors to
| [`hx-get`](/attributes/hx-get) | issues a `GET` to the specified URL
| [`hx-history-elt`](/attributes/hx-history-elt) | the element to snapshot and restore during history navigation
| [`hx-include`](/attributes/hx-include) | includes additional data in AJAX requests
| [`hx-indicator`](/attributes/hx-indicator) | the element to put the `htmx-request` class on during the AJAX request
| [`hx-params`](/attributes/hx-params) | filters the parameters that will be submitted with a request
| [`hx-patch`](/attributes/hx-patch) | issues a `PATCH` to the specified URL
| [`hx-post`](/attributes/hx-post) | issues a `POST` to the specified URL
| [`hx-prompt`](/attributes/hx-prompt) | shows a prompt before submitting a request
| [`hx-push-url`](/attributes/hx-push-url) | pushes the URL into the location bar, creating a new history entry
| [`hx-put`](/attributes/hx-put) | issues a `PUT` to the specified URL
| [`hx-select`](/attributes/hx-select) | selects a subset of the server response to process
| [`hx-sse-src`](/attributes/hx-sse-src) | establishes an [SSE](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events) source for events
| [`hx-swap-oob`](/attributes/hx-swap-oob) | marks content in a response as being "Out of Band", i.e. swapped somewhere other than the target
| [`hx-swap`](/attributes/hx-swap) | controls how the response content is swapped into the DOM (e.g. 'outerHTML' or 'beforeEnd')
| [`hx-target`](/attributes/hx-target) | specifies the target element to be swapped
| [`hx-trigger`](/attributes/hx-trigger) | specifies the event that triggers the request
## <a name="classes"></a> [CSS Class Reference](#classes)
| Class | Description |
|-----------|-------------|
| `kutty-indicator` | A dynamically generated class that will toggle visible (opacity:1) when a `kutty-request` class is present
| `kutty-request` | Applied to either the element or the element specified with [`kt-indicator`](/attributes/kt-indicator) while a request is ongoing
| `kutty-settling` | Applied to a target after content is swapped, removed after it is settled
| `kutty-swapping` | Applied to a target before any content is swapped, removed after it is swapped
| `htmx-indicator` | A dynamically generated class that will toggle visible (opacity:1) when a `htmx-request` class is present
| `htmx-request` | Applied to either the element or the element specified with [`hx-indicator`](/attributes/hx-indicator) while a request is ongoing
| `htmx-settling` | Applied to a target after content is swapped, removed after it is settled
| `htmx-swapping` | Applied to a target before any content is swapped, removed after it is swapped
## <a name="headers"></a> [HTTP Header Reference](#headers)
@ -46,48 +46,48 @@ title: </> kutty - Attributes
| Header | Description |
|-------|-------------|
| `X-HTTP-Method-Override` | the HTTP verb for non-`GET` and `POST` requests
| `X-KT-Active-Element-Name` | the `name` of the active element if it exists
| `X-KT-Active-Element-Value` | the `value` of the active element if it exists
| `X-KT-Active-Element` | the `id` of the active element if it exists
| `X-KT-Current-URL` | the current URL of the browser
| `X-KT-Event-Target` | the `id` of the original event target
| `X-KT-Prompt` | the user response to an [ic-prompt](/attributes/kt-prompt)
| `X-KT-Request` | always `true`
| `X-KT-Target` | the `id` of the target element if it exists
| `X-KT-Trigger-Name` | the `name` of the triggered element if it exists
| `X-KT-Trigger` | the `id` of the triggered element if it exists
| `X-HX-Active-Element-Name` | the `name` of the active element if it exists
| `X-HX-Active-Element-Value` | the `value` of the active element if it exists
| `X-HX-Active-Element` | the `id` of the active element if it exists
| `X-HX-Current-URL` | the current URL of the browser
| `X-HX-Event-Target` | the `id` of the original event target
| `X-HX-Prompt` | the user response to an [ic-prompt](/attributes/hx-prompt)
| `X-HX-Request` | always `true`
| `X-HX-Target` | the `id` of the target element if it exists
| `X-HX-Trigger-Name` | the `name` of the triggered element if it exists
| `X-HX-Trigger` | the `id` of the triggered element if it exists
### <a name="response_headers"></a> [Response Headers](#response_headers)
| Header | Description |
|-------|-------------|
| `X-KT-Push` | pushes a new url into the history stack
| [`X-KT-Trigger`](/headers/x-kt-trigger) | allows you to trigger client side events, see the [documentation](/headers/x-kt-trigger) for more info
| `X-HX-Push` | pushes a new url into the history stack
| [`X-HX-Trigger`](/headers/x-hx-trigger) | allows you to trigger client side events, see the [documentation](/headers/x-hx-trigger) for more info
## <a name="events"></a> [Event Reference](#events)
| Event | Description |
|-------|-------------|
| [`afterOnLoad.kutty`](/events#afterOnLoad.kutty) | triggered after an AJAX request has finished
| [`afterSettle.kutty`](/events#afterSettle.kutty) | triggered after the DOM has settled
| [`afterSwap.kutty`](/events#afterSwap.kutty) | triggered after new content has been swapped in
| [`beforeOnLoad.kutty`](/events#beforeOnLoad.kutty) | triggered before any response processing occurs
| [`beforeRequest.kutty`](/events#beforeRequest.kutty) | triggered before an AJAX request is made
| [`beforeSwap.kutty`](/events#beforeSwap.kutty) | triggered before a swap is done
| [`configRequest.kutty`](/events#configRequest.kutty) | triggered before the request, allows you to customize parameters, headers
| [`historyCacheMiss.kutty`](/events#historyCacheMiss.kutty) | triggered on a cache miss in the history subsystem
| [`historyCacheMissError.kutty`](/events#historyCacheMissError.kutty) | triggered on a unsuccessful remote retrieval
| [`historyCacheMissLoad.kutty`](/events#historyCacheMissLoad.kutty) | triggered on a succesful remote retrieval
| [`historyRestore.kutty`](/events#historyRestore.kutty) | triggered when kutty handles a history restoration action
| [`beforeHistorySave.kutty`](/events#beforeHistorySave.kutty) | triggered before content is saved to the history cache
| [`initSSE.kutty`](/events#initSSE.kutty) | triggered when a new Server Sent Event source is created
| [`load.kutty`](/events#load.kutty) | triggered when new content is added to the DOM
| [`noSSESourceError.kutty`](/events#noSSESourceError.kutty) | triggered when an element refers to a SSE event in its trigger, but no parent SSE source has been defined
| [`onLoadError.kutty`](/events#onLoadError.kutty) | triggered when an exception occurs during the onLoad handling in kutty
| [`oobErrorNoTarget.kutty`](/events#oobErrorNoTarget.kutty) | triggered when an out of band element does not have a matching ID in the current DOM
| [`prompt.kutty`](/events#prompt.kutty) | triggered after a prompt is shown
| [`responseError.kutty`](/events#responseError.kutty) | triggered when an HTTP response error (non-`200` or `300` response code) occurs
| [`sendError.kutty`](/events#sendError.kutty) | triggered when a network error prevents an HTTP request from happening
| [`sseError.kutty`](/events#sseError.kutty) | triggered when an error occurs with a SSE source
| [`swapError.kutty`](/events#swapError.kutty) | triggered when an error occurs during the swap phase
| [`targetError.kutty`](/events#targetError.kutty) | triggered when an invalid target is specified
| [`afterOnLoad.htmx`](/events#afterOnLoad.htmx) | triggered after an AJAX request has finished
| [`afterSettle.htmx`](/events#afterSettle.htmx) | triggered after the DOM has settled
| [`afterSwap.htmx`](/events#afterSwap.htmx) | triggered after new content has been swapped in
| [`beforeOnLoad.htmx`](/events#beforeOnLoad.htmx) | triggered before any response processing occurs
| [`beforeRequest.htmx`](/events#beforeRequest.htmx) | triggered before an AJAX request is made
| [`beforeSwap.htmx`](/events#beforeSwap.htmx) | triggered before a swap is done
| [`configRequest.htmx`](/events#configRequest.htmx) | triggered before the request, allows you to customize parameters, headers
| [`historyCacheMiss.htmx`](/events#historyCacheMiss.htmx) | triggered on a cache miss in the history subsystem
| [`historyCacheMissError.htmx`](/events#historyCacheMissError.htmx) | triggered on a unsuccessful remote retrieval
| [`historyCacheMissLoad.htmx`](/events#historyCacheMissLoad.htmx) | triggered on a succesful remote retrieval
| [`historyRestore.htmx`](/events#historyRestore.htmx) | triggered when htmx handles a history restoration action
| [`beforeHistorySave.htmx`](/events#beforeHistorySave.htmx) | triggered before content is saved to the history cache
| [`initSSE.htmx`](/events#initSSE.htmx) | triggered when a new Server Sent Event source is created
| [`load.htmx`](/events#load.htmx) | triggered when new content is added to the DOM
| [`noSSESourceError.htmx`](/events#noSSESourceError.htmx) | triggered when an element refers to a SSE event in its trigger, but no parent SSE source has been defined
| [`onLoadError.htmx`](/events#onLoadError.htmx) | triggered when an exception occurs during the onLoad handling in htmx
| [`oobErrorNoTarget.htmx`](/events#oobErrorNoTarget.htmx) | triggered when an out of band element does not have a matching ID in the current DOM
| [`prompt.htmx`](/events#prompt.htmx) | triggered after a prompt is shown
| [`responseError.htmx`](/events#responseError.htmx) | triggered when an HTTP response error (non-`200` or `300` response code) occurs
| [`sendError.htmx`](/events#sendError.htmx) | triggered when a network error prevents an HTTP request from happening
| [`sseError.htmx`](/events#sseError.htmx) | triggered when an error occurs with a SSE source
| [`swapError.htmx`](/events#swapError.htmx) | triggered when an error occurs during the swap phase
| [`targetError.htmx`](/events#targetError.htmx) | triggered when an invalid target is specified

View File

@ -1,21 +1,21 @@
---
layout: layout.njk
title: </> kutty - high power tools for html
title: </> htmx - high power tools for html
---
## Kutty Talk
## Htmx Talk
Right now the best place to talk about kutty is the [intercooler gitter room](https://gitter.im/intercooler-js/Lobby)
Right now the best place to talk about htmx is the [intercooler gitter room](https://gitter.im/intercooler-js/Lobby)
I'll be setting up a forum and chat room at some point.
## Features & Bug Reports
[https://github.com/bigskysoftware/kutty/issues](https://github.com/bigskysoftware/kutty/issues)
[https://github.com/bigskysoftware/htmx/issues](https://github.com/bigskysoftware/htmx/issues)
## Twitter
[@kutty_org](https://twitter.com/kutty_org)
[@htmx_org](https://twitter.com/htmx_org)
## Blog & Announcements