le big rename

This commit is contained in:
carson
2020-05-06 17:16:20 -07:00
parent ef174f45d4
commit b003ccadf8
29 changed files with 2543 additions and 473 deletions

View File

@@ -1,12 +1,9 @@
# </> HTMx
HTML Extensions
# </> kutty
*High power tools for HTML*
## Overview
HTMx allows you to issue AJAX requests from HTML without needing to write any javascript. It is a set of extensions
to HTML (attributes, request headers, etc.) that allow you to use markup for more complex user interfaces and
features, while at the same time retaining the simplicity of the original web programming model.
Kutty is a set of HTML extensions (attributes, request headers, etc.) that allow you to use markup for complex user
interfaces and features, while retaining the simplicity and power of the hypertext we all know and love.
## Background
HTMx is inspired by [intercooler.js](http://intercoolerjs.org), but aims to simplify that library and include no dependencies.
Kutty is the successor to [intercooler.js](http://intercoolerjs.org)

19
TODO.md
View File

@@ -1,14 +1,8 @@
# &lt;/> HTMx
*HTML Extensions*
## GOALS
* Dependency-free implementation of intercooler.js-like HTML-driven AJAX functionality
* Minimalist functionality, rely heavily on built in functionality
* Support IE10+
** CSS transitions only
** Pluggable event model
* < 10k in .min form
* Dependency-free implementation of intercooler.js
* Support IE11 (stretch: IE10)
* < 10k in .min.gz form
## TODOS
@@ -21,9 +15,9 @@
* Testing
* polling
* merge
* hx-select
* kt-select
* history
* hx-boost
* kt-boost
* transition model for content swaps
* build website with 11ty
* landing page
@@ -47,5 +41,4 @@
* http://youmightnotneedjquery.com/
* http://intercoolerjs.org/docs.html
* http://intercoolerjs.org/reference.html
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap

1996
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,15 +1,14 @@
{
"name": "htmx.org",
"description": "HTML Extensions",
"name": "kutty.org",
"description": "high power tools for html",
"keywords": [
"htmx",
"AJAX",
"HTML"
],
"version": "0.0.2",
"homepage": "https://htmx.org/",
"version": "0.0.1",
"homepage": "https://kutty.org/",
"bugs": {
"url": "https://github.com/bigskysoftware/htmx/issues"
"url": "https://github.com/bigskysoftware/kutty/issues"
},
"license": "BSD 2-Clause",
"files": [
@@ -17,26 +16,27 @@
"README.md",
"dist/*.js"
],
"main": "dist/htmx.min.js",
"unpkg": "dist/htmx.min.js",
"main": "dist/kutty.min.js",
"unpkg": "dist/kutty.min.js",
"scripts": {
"test": "mocha-chrome test/index.html",
"dist": "cp src/htmx.js dist/ && npm run-script uglify && gzip -k -f dist/htmx.min.js > dist/htmx.min.js.gz && exit",
"dist": "cp src/cutty.js dist/ && npm run-script uglify && gzip -k -f dist/cutty.min.js > dist/kutty.min.js.gz && exit",
"www": "node scripts/www.js",
"uglify": "uglifyjs -m eval -o dist/htmx.min.js dist/htmx.js"
"uglify": "uglifyjs -m eval -o dist/kutty.min.js dist/kutty.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/bigskysoftware/htmx.git"
"url": "git+https://github.com/bigskysoftware/kutty.git"
},
"devDependencies": {
"@11ty/eleventy": "^0.10.0",
"@11ty/eleventy-plugin-syntaxhighlight": "^3.0.1",
"chai": "^4.2.0",
"eleventy-plugin-sass": "^1.0.0",
"fs-extra": "^9.0.0",
"mocha": "^7.1.1",
"mocha-chrome": "^2.2.0",
"sinon": "^9.0.2",
"uglify-js": "^3.9.1",
"fs-extra": "^9.0.0"
"uglify-js": "^3.9.1"
}
}

View File

@@ -1,5 +1,5 @@
// noinspection JSUnusedAssignment
var HTMx = HTMx || (function () {
var kutty = kutty || (function () {
'use strict';
var VERBS = ['get', 'post', 'put', 'delete', 'patch']
@@ -24,7 +24,7 @@ var HTMx = HTMx || (function () {
return elt.getAttribute && elt.getAttribute(name);
}
// resolve with both hx and data-hx prefixes
// resolve with both kt and data-kt prefixes
function getAttributeValue(elt, qualifiedName) {
return getRawAttribute(elt, qualifiedName) || getRawAttribute(elt, "data-" + qualifiedName);
}
@@ -87,7 +87,7 @@ var HTMx = HTMx || (function () {
}
function getInternalData(elt) {
var dataProp = 'hx-data-internal';
var dataProp = 'kutty-internal-data';
var data = elt[dataProp];
if (!data) {
data = elt[dataProp] = {};
@@ -129,9 +129,9 @@ var HTMx = HTMx || (function () {
//====================================================================
function getTarget(elt) {
var explicitTarget = getClosestMatch(elt, function(e){return getRawAttribute(e,"hx-target") !== null});
var explicitTarget = getClosestMatch(elt, function(e){return getRawAttribute(e,"kt-target") !== null});
if (explicitTarget) {
var targetStr = getRawAttribute(explicitTarget, "hx-target");
var targetStr = getRawAttribute(explicitTarget, "kt-target");
if (targetStr === "this") {
return explicitTarget;
} else {
@@ -161,7 +161,7 @@ var HTMx = HTMx || (function () {
function handleOutOfBandSwaps(fragment) {
var settleTasks = [];
forEach(fragment.children, function(child){
if (getAttributeValue(child, "hx-swap-oob") === "true") {
if (getAttributeValue(child, "kt-swap-oob") === "true") {
var target = getDocument().getElementById(child.id);
if (target) {
var fragment = new DocumentFragment()
@@ -169,7 +169,7 @@ var HTMx = HTMx || (function () {
settleTasks = settleTasks.concat(swapOuterHTML(target, fragment));
} else {
child.parentNode.removeChild(child);
triggerEvent(getDocument().body, "oobErrorNoTarget.hx", {id:child.id, content:child})
triggerEvent(getDocument().body, "oobErrorNoTarget.kutty", {id:child.id, content:child})
}
}
})
@@ -197,7 +197,7 @@ var HTMx = HTMx || (function () {
var child = fragment.firstChild;
parentNode.insertBefore(child, insertBefore);
if (child.nodeType !== Node.TEXT_NODE) {
triggerEvent(child, 'load.hx', {elt:child, parent:parentElt(child)});
triggerEvent(child, 'load.kutty', {elt:child, parent:parentElt(child)});
processNode(child);
}
}
@@ -243,7 +243,7 @@ var HTMx = HTMx || (function () {
}
function maybeSelectFromResponse(elt, fragment) {
var selector = getClosestAttributeValue(elt, "hx-select");
var selector = getClosestAttributeValue(elt, "kt-select");
if (selector) {
var newFragment = new DocumentFragment();
forEach(fragment.querySelectorAll(selector), function (node) {
@@ -260,7 +260,7 @@ var HTMx = HTMx || (function () {
fragment = maybeSelectFromResponse(elt, fragment);
var swapStyle = getClosestAttributeValue(elt, "hx-swap");
var swapStyle = getClosestAttributeValue(elt, "kt-swap");
switch(swapStyle) {
case "outerHTML": return concat(settleTasks, swapOuterHTML(target, fragment));
case "prepend": return concat(settleTasks, swapPrepend(target, fragment));
@@ -291,7 +291,7 @@ var HTMx = HTMx || (function () {
}
function getTrigger(elt) {
var explicitTrigger = getClosestAttributeValue(elt, 'hx-trigger');
var explicitTrigger = getClosestAttributeValue(elt, 'kt-trigger');
if (explicitTrigger) {
return explicitTrigger;
} else {
@@ -337,7 +337,7 @@ var HTMx = HTMx || (function () {
nodeData.timeout = setTimeout(function () {
if (bodyContains(elt)) {
issueAjaxRequest(elt, verb, path);
processPolling(elt, verb, getAttributeValue(elt, "hx-" + verb));
processPolling(elt, verb, getAttributeValue(elt, "kt-" + verb));
}
}, interval);
}
@@ -373,14 +373,14 @@ var HTMx = HTMx || (function () {
var elementData = getInternalData(elt);
if (!eventData.handled) {
eventData.handled = true;
if (getAttributeValue(elt, "hx-trigger-once") === "true") {
if (getAttributeValue(elt, "kt-trigger-once") === "true") {
if (elementData.triggeredOnce) {
return;
} else {
elementData.triggeredOnce = true;
}
}
if (getAttributeValue(elt, "hx-trigger-changed-only") === "true") {
if (getAttributeValue(elt, "kt-trigger-changed-only") === "true") {
if (elementData.lastValue === elt.value) {
return;
} else {
@@ -390,7 +390,7 @@ var HTMx = HTMx || (function () {
if (elementData.delayed) {
clearTimeout(elementData.delayed);
}
var eventDelay = getAttributeValue(elt, "hx-trigger-delay");
var eventDelay = getAttributeValue(elt, "kt-trigger-delay");
var issueRequest = function(){
issueAjaxRequest(elt, verb, path, evt.target);
}
@@ -407,13 +407,13 @@ var HTMx = HTMx || (function () {
}
function initScrollHandler() {
if (!window['hxScrollHandler']) {
if (!window['kuttyScrollHandler']) {
var scrollHandler = function() {
forEach(getDocument().querySelectorAll("[hx-trigger='reveal']"), function (elt) {
forEach(getDocument().querySelectorAll("[kt-trigger='reveal']"), function (elt) {
maybeReveal(elt);
});
};
window['hxScrollHandler'] = scrollHandler;
window['kuttyScrollHandler'] = scrollHandler;
window.addEventListener("scroll", scrollHandler)
}
}
@@ -438,10 +438,10 @@ var HTMx = HTMx || (function () {
initializer: function() { new EventSource(sseSrc, details.config) },
config:{withCredentials: true}
};
triggerEvent(elt, "initSSE.mx", {config:details})
triggerEvent(elt, "initSSE.kutty", {config:details})
var source = details.initializer();
source.onerror = function (e) {
triggerEvent(elt, "sseError.mx", {error:e, source:source});
triggerEvent(elt, "sseError.kutty", {error:e, source:source});
maybeCloseSSESource(elt);
};
getInternalData(elt).sseSource = source;
@@ -463,7 +463,7 @@ var HTMx = HTMx || (function () {
};
sseSource.sseSource.addEventListener(sseEventName, sseListener);
} else {
triggerEvent(elt, "noSSESourceError.mx")
triggerEvent(elt, "noSSESourceError.kutty")
}
}
@@ -477,7 +477,7 @@ var HTMx = HTMx || (function () {
function processVerbs(elt, nodeData, trigger) {
var explicitAction = false;
forEach(VERBS, function (verb) {
var path = getAttributeValue(elt, 'hx-' + verb);
var path = getAttributeValue(elt, 'kt-' + verb);
if (path) {
explicitAction = true;
nodeData.path = path;
@@ -508,18 +508,18 @@ var HTMx = HTMx || (function () {
var trigger = getTrigger(elt);
var explicitAction = processVerbs(elt, nodeData, trigger);
if (!explicitAction && getClosestAttributeValue(elt, "hx-boost") === "true") {
if (!explicitAction && getClosestAttributeValue(elt, "kt-boost") === "true") {
boostElement(elt, nodeData, trigger);
}
var sseSrc = getAttributeValue(elt, 'hx-sse-source');
var sseSrc = getAttributeValue(elt, 'kt-sse-source');
if (sseSrc) {
initSSESource(elt, sseSrc);
}
var addClass = getAttributeValue(elt, 'hx-add-class');
var addClass = getAttributeValue(elt, 'kt-add-class');
if (addClass) {
processClassList(elt, addClass, "add");
}
var removeClass = getAttributeValue(elt, 'hx-remove-class');
var removeClass = getAttributeValue(elt, 'kt-remove-class');
if (removeClass) {
processClassList(elt, removeClass, "remove");
}
@@ -532,7 +532,7 @@ var HTMx = HTMx || (function () {
//====================================================================
function sendError(elt, eventName, details) {
var errorURL = getClosestAttributeValue(elt, "hx-error-url");
var errorURL = getClosestAttributeValue(elt, "kt-error-url");
if (errorURL) {
var xhr = new XMLHttpRequest();
xhr.open("POST", errorURL);
@@ -555,22 +555,22 @@ var HTMx = HTMx || (function () {
function triggerEvent(elt, eventName, details) {
details["elt"] = elt;
var event = makeEvent(eventName, details);
if (HTMx.logger) {
HTMx.logger(elt, eventName, details);
if (kutty.logger) {
kutty.logger(elt, eventName, details);
if (eventName.indexOf("Error") > 0) {
sendError(elt, eventName, details);
}
}
var eventResult = elt.dispatchEvent(event);
var allResult = elt.dispatchEvent(makeEvent("all.hx", {elt:elt, originalDetails:details, originalEvent: event}));
var allResult = elt.dispatchEvent(makeEvent("all.kutty", {elt:elt, originalDetails:details, originalEvent: event}));
return eventResult && allResult;
}
function addHTMxEventListener(arg1, arg2, arg3) {
function addKuttyEventListener(arg1, arg2, arg3) {
var target, event, listener;
if (isFunction(arg1)) {
target = getDocument().body;
event = "all.hx";
event = "all.kutty";
listener = arg1;
} else if (isFunction(arg2)) {
target = getDocument().body;
@@ -588,7 +588,7 @@ var HTMx = HTMx || (function () {
// History Support
//====================================================================
function getHistoryElement() {
var historyElt = getDocument().querySelector('.hx-history-element');
var historyElt = getDocument().querySelector('.kt-history-element');
return historyElt || getDocument().body;
}
@@ -607,21 +607,21 @@ var HTMx = HTMx || (function () {
}
function bumpHistoryAccessDate(pathAndSearch) {
var historyTimestamps = JSON.parse(localStorage.getItem("hx-history-timestamps")) || {};
var historyTimestamps = JSON.parse(localStorage.getItem("kt-history-timestamps")) || {};
historyTimestamps[pathAndSearch] = Date.now();
var paths = Object.keys(historyTimestamps);
if (paths.length > 20) {
purgeOldestPaths(paths, historyTimestamps);
}
localStorage.setItem("hx-history-timestamps", JSON.stringify(historyTimestamps));
localStorage.setItem("kt-history-timestamps", JSON.stringify(historyTimestamps));
}
function saveHistory() {
var elt = getHistoryElement();
var pathAndSearch = location.pathname+location.search;
triggerEvent(getDocument().body, "historyUpdate.hx", {path:pathAndSearch, historyElement:elt});
triggerEvent(getDocument().body, "historyUpdate.kutty", {path:pathAndSearch, historyElement:elt});
history.replaceState({}, getDocument().title, window.location.href);
localStorage.setItem('hx-history:' + pathAndSearch, elt.innerHTML);
localStorage.setItem('kt-history:' + pathAndSearch, elt.innerHTML);
bumpHistoryAccessDate(pathAndSearch);
}
@@ -636,14 +636,14 @@ var HTMx = HTMx || (function () {
}
function loadHistoryFromServer(pathAndSearch) {
triggerEvent(getDocument().body, "historyCacheMiss.hx", {path: pathAndSearch});
triggerEvent(getDocument().body, "historyCacheMiss.kutty", {path: pathAndSearch});
var request = new XMLHttpRequest();
request.open('GET', pathAndSearch, true);
request.onload = function () {
triggerEvent(getDocument().body, "historyCacheMissLoad.hx", {path: pathAndSearch});
triggerEvent(getDocument().body, "historyCacheMissLoad.kutty", {path: pathAndSearch});
if (this.status >= 200 && this.status < 400) {
var fragment = makeFragment(this.response);
fragment = fragment.querySelector('.hx-history-element') || fragment;
fragment = fragment.querySelector('.kt-history-element') || fragment;
settleImmediately(swapInnerHTML(getHistoryElement(), fragment));
}
};
@@ -651,8 +651,8 @@ var HTMx = HTMx || (function () {
function restoreHistory() {
var pathAndSearch = location.pathname+location.search;
triggerEvent(getDocument().body, "historyRestore.hx", {path:pathAndSearch});
var content = localStorage.getItem('hx-history:' + pathAndSearch);
triggerEvent(getDocument().body, "historyRestore.kutty", {path:pathAndSearch});
var content = localStorage.getItem('kt-history:' + pathAndSearch);
if (content) {
bumpHistoryAccessDate(pathAndSearch);
settleImmediately(swapInnerHTML(getHistoryElement(), makeFragment(content)));
@@ -662,7 +662,7 @@ var HTMx = HTMx || (function () {
}
function shouldPush(elt) {
return getClosestAttributeValue(elt, "hx-push-url") === "true" ||
return getClosestAttributeValue(elt, "kt-push-url") === "true" ||
(elt.tagName === "A" && getInternalData(elt).boosted);
}
@@ -675,14 +675,14 @@ var HTMx = HTMx || (function () {
}
function mutateRequestIndicatorClasses(elt, action) {
var indicator = getClosestAttributeValue(elt, 'hx-indicator');
var indicator = getClosestAttributeValue(elt, 'kt-indicator');
if (indicator) {
var indicators = getDocument().querySelectorAll(indicator);
} else {
indicators = [elt];
}
forEach(indicators, function(ic) {
ic.classList[action].call(ic.classList, "hx-show-indicator");
ic.classList[action].call(ic.classList, "kutty-show-indicator");
});
}
@@ -735,7 +735,7 @@ var HTMx = HTMx || (function () {
processInputValue(processed, values, elt);
// include any explicit includes
var includes = getAttributeValue(elt, "hx-include");
var includes = getAttributeValue(elt, "kt-include");
if (includes) {
var nodes = getDocument().querySelectorAll(includes);
forEach(nodes, function(node) {
@@ -778,7 +778,7 @@ var HTMx = HTMx || (function () {
//====================================================================
function setHeader(xhr, name, value, noPrefix) {
xhr.setRequestHeader((noPrefix ? "" : "X-HX-") + name, value || "");
xhr.setRequestHeader((noPrefix ? "" : "X-KT-") + name, value || "");
}
function setRequestHeaders(xhr, elt, target, prompt, eventTarget) {
@@ -813,13 +813,13 @@ var HTMx = HTMx || (function () {
eltData.requestInFlight = false
}
var target = getTarget(elt);
var promptQuestion = getClosestAttributeValue(elt, "hx-prompt");
var promptQuestion = getClosestAttributeValue(elt, "kt-prompt");
if (promptQuestion) {
var prompt = prompt(promptQuestion);
if(!triggerEvent(elt, 'prompt.hx', {prompt: prompt, target:target})) return endRequestLock();
if(!triggerEvent(elt, 'prompt.kutty', {prompt: prompt, target:target})) return endRequestLock();
}
var confirmQuestion = getClosestAttributeValue(elt, "hx-confirm");
var confirmQuestion = getClosestAttributeValue(elt, "kt-confirm");
if (confirmQuestion) {
if(!confirm(confirmQuestion)) return endRequestLock();
}
@@ -827,7 +827,7 @@ var HTMx = HTMx || (function () {
var xhr = new XMLHttpRequest();
var inputValues = getInputValues(elt);
if(!triggerEvent(elt, 'values.hx', {values: inputValues, target:target})) return endRequestLock();
if(!triggerEvent(elt, 'values.kutty', {values: inputValues, target:target})) return endRequestLock();
// request type
var requestURL;
@@ -852,10 +852,10 @@ var HTMx = HTMx || (function () {
xhr.onload = function () {
try {
if (!triggerEvent(elt, 'beforeOnLoad.hx', {xhr: xhr, target: target})) return;
if (!triggerEvent(elt, 'beforeOnLoad.kutty', {xhr: xhr, target: target})) return;
handleTrigger(elt, this.getResponseHeader("X-HX-Trigger"));
var pushedUrl = this.getResponseHeader("X-HX-Push")
handleTrigger(elt, this.getResponseHeader("X-KT-Trigger"));
var pushedUrl = this.getResponseHeader("X-KT-Push")
var shouldSaveHistory = shouldPush(elt) || pushedUrl;
@@ -864,47 +864,47 @@ var HTMx = HTMx || (function () {
if (this.status !== 204) {
// Success!
var resp = this.response;
if (!triggerEvent(elt, 'beforeSwap.hx', {xhr: xhr, target: target})) return;
if (!triggerEvent(elt, 'beforeSwap.kutty', {xhr: xhr, target: target})) return;
// Save current page
if (shouldSaveHistory) {
saveHistory();
}
target.classList.add("hx-swapping");
target.classList.add("kutty-swapping");
var doSwap = function () {
try {
var settleTasks = swapResponse(target, elt, resp);
target.classList.remove("hx-swapping");
target.classList.add("hx-settling");
triggerEvent(elt, 'afterSwap.hx', {xhr: xhr, target: target});
target.classList.remove("kutty-swapping");
target.classList.add("kutty-settling");
triggerEvent(elt, 'afterSwap.kutty', {xhr: xhr, target: target});
var doSettle = function(){
forEach(settleTasks, function (settleTask) {
settleTask.call();
});
target.classList.remove("hx-settling");
target.classList.remove("kutty-settling");
// push URL and save new page
if (shouldSaveHistory) {
pushUrlIntoHistory(pushedUrl || requestURL );
saveHistory();
}
triggerEvent(elt, 'afterSettle.hx', {xhr: xhr, target: target});
triggerEvent(elt, 'afterSettle.kutty', {xhr: xhr, target: target});
}
var settleDelayStr = getAttributeValue(elt, "hx-settle-delay") || "100ms";
var settleDelayStr = getAttributeValue(elt, "kt-settle-delay") || "100ms";
if (settleDelayStr) {
setTimeout(doSettle, parseInterval(settleDelayStr))
} else {
doSettle();
}
} catch (e) {
triggerEvent(elt, 'swapError.hx', {xhr: xhr, response: xhr.response, status: xhr.status, target: target});
triggerEvent(elt, 'swapError.kutty', {xhr: xhr, response: xhr.response, status: xhr.status, target: target});
throw e;
}
};
var swapDelayStr = getAttributeValue(elt, "hx-swap-delay");
var swapDelayStr = getAttributeValue(elt, "kt-swap-delay");
if (swapDelayStr) {
setTimeout(doSwap, parseInterval(swapDelayStr))
} else {
@@ -912,22 +912,22 @@ var HTMx = HTMx || (function () {
}
}
} else {
triggerEvent(elt, 'responseError.hx', {xhr: xhr, response: xhr.response, status: xhr.status, target: target});
triggerEvent(elt, 'responseError.kutty', {xhr: xhr, response: xhr.response, status: xhr.status, target: target});
}
} catch (e) {
triggerEvent(elt, 'onLoadError.hx', {xhr: xhr, response: xhr.response, status: xhr.status, target: target});
triggerEvent(elt, 'onLoadError.kutty', {xhr: xhr, response: xhr.response, status: xhr.status, target: target});
throw e;
} finally {
removeRequestIndicatorClasses(elt);
endRequestLock();
triggerEvent(elt, 'afterOnLoad.hx', {xhr: xhr, response: xhr.response, status: xhr.status, target: target});
triggerEvent(elt, 'afterOnLoad.kutty', {xhr: xhr, response: xhr.response, status: xhr.status, target: target});
}
}
xhr.onerror = function () {
removeRequestIndicatorClasses(elt);triggerEvent(elt, 'loadError.hx', {xhr:xhr});
removeRequestIndicatorClasses(elt);triggerEvent(elt, 'loadError.kutty', {xhr:xhr});
endRequestLock();
}
if(!triggerEvent(elt, 'beforeRequest.hx', {xhr:xhr, values: inputValues, target:target})) return endRequestLock();
if(!triggerEvent(elt, 'beforeRequest.kutty', {xhr:xhr, values: inputValues, target:target})) return endRequestLock();
addRequestIndicatorClasses(elt);
xhr.send(verb === 'get' ? null : urlEncode(inputValues));
}
@@ -959,8 +959,8 @@ var HTMx = HTMx || (function () {
// Public API
return {
processElement: processNode,
on: addHTMxEventListener,
version: "0.0.2",
on: addKuttyEventListener,
version: "0.0.1",
_:internalEval
}
}

View File

@@ -1,4 +1,4 @@
describe("HTMx AJAX Tests", function(){
describe("kutty AJAX Tests", function(){
beforeEach(function() {
this.server = makeServer();
clearWorkArea();
@@ -13,7 +13,7 @@ describe("HTMx AJAX Tests", function(){
{
this.server.respondWith("GET", "/test", "Clicked!");
var btn = make('<button hx-get="/test">Click Me!</button>')
var btn = make('<button kt-get="/test">Click Me!</button>')
btn.click();
this.server.respond();
btn.innerHTML.should.equal("Clicked!");
@@ -21,13 +21,13 @@ describe("HTMx AJAX Tests", function(){
it('processes inner content properly', function()
{
this.server.respondWith("GET", "/test", '<a hx-get="/test2">Click Me</a>');
this.server.respondWith("GET", "/test", '<a kt-get="/test2">Click Me</a>');
this.server.respondWith("GET", "/test2", "Clicked!");
var div = make('<div hx-get="/test"></div>')
var div = make('<div kt-get="/test"></div>')
div.click();
this.server.respond();
div.innerHTML.should.equal('<a hx-get="/test2">Click Me</a>');
div.innerHTML.should.equal('<a kt-get="/test2">Click Me</a>');
var a = div.querySelector('a');
a.click();
this.server.respond();
@@ -36,10 +36,10 @@ describe("HTMx AJAX Tests", function(){
it('handles swap outerHTML properly', function()
{
this.server.respondWith("GET", "/test", '<a id="a1" hx-get="/test2">Click Me</a>');
this.server.respondWith("GET", "/test", '<a id="a1" kt-get="/test2">Click Me</a>');
this.server.respondWith("GET", "/test2", "Clicked!");
var div = make('<div id="d1" hx-get="/test" hx-swap="outerHTML"></div>')
var div = make('<div id="d1" kt-get="/test" kt-swap="outerHTML"></div>')
div.click();
should.equal(byId("d1"), div);
this.server.respond();
@@ -54,11 +54,11 @@ describe("HTMx AJAX Tests", function(){
var i = 0;
this.server.respondWith("GET", "/test", function(xhr){
i++;
xhr.respond(200, {}, '<a id="a' + i + '" hx-get="/test2" hx-swap="innerHTML">' + i + '</a>');
xhr.respond(200, {}, '<a id="a' + i + '" kt-get="/test2" kt-swap="innerHTML">' + i + '</a>');
});
this.server.respondWith("GET", "/test2", "*");
var div = make('<div hx-get="/test" hx-swap="prependBefore">*</div>')
var div = make('<div kt-get="/test" kt-swap="prependBefore">*</div>')
var parent = div.parentElement;
div.click();
this.server.respond();
@@ -84,11 +84,11 @@ describe("HTMx AJAX Tests", function(){
var i = 0;
this.server.respondWith("GET", "/test", function(xhr){
i++;
xhr.respond(200, {}, '<a id="a' + i + '" hx-get="/test2" hx-swap="innerHTML">' + i + '</a>');
xhr.respond(200, {}, '<a id="a' + i + '" kt-get="/test2" kt-swap="innerHTML">' + i + '</a>');
});
this.server.respondWith("GET", "/test2", "*");
var div = make('<div hx-get="/test" hx-swap="prepend">*</div>')
var div = make('<div kt-get="/test" kt-swap="prepend">*</div>')
div.click();
this.server.respond();
div.innerText.should.equal("1*");
@@ -111,11 +111,11 @@ describe("HTMx AJAX Tests", function(){
var i = 0;
this.server.respondWith("GET", "/test", function(xhr){
i++;
xhr.respond(200, {}, '<a id="a' + i + '" hx-get="/test2" hx-swap="innerHTML">' + i + '</a>');
xhr.respond(200, {}, '<a id="a' + i + '" kt-get="/test2" kt-swap="innerHTML">' + i + '</a>');
});
this.server.respondWith("GET", "/test2", "*");
var div = make('<div hx-get="/test" hx-swap="prepend"></div>')
var div = make('<div kt-get="/test" kt-swap="prepend"></div>')
div.click();
this.server.respond();
div.innerText.should.equal("1");
@@ -138,11 +138,11 @@ describe("HTMx AJAX Tests", function(){
var i = 0;
this.server.respondWith("GET", "/test", function(xhr){
i++;
xhr.respond(200, {}, '<a id="a' + i + '" hx-get="/test2" hx-swap="innerHTML">' + i + '</a>');
xhr.respond(200, {}, '<a id="a' + i + '" kt-get="/test2" kt-swap="innerHTML">' + i + '</a>');
});
this.server.respondWith("GET", "/test2", "*");
var div = make('<div hx-get="/test" hx-swap="appendAfter">*</div>')
var div = make('<div kt-get="/test" kt-swap="appendAfter">*</div>')
var parent = div.parentElement;
div.click();
this.server.respond();
@@ -168,11 +168,11 @@ describe("HTMx AJAX Tests", function(){
var i = 0;
this.server.respondWith("GET", "/test", function(xhr){
i++;
xhr.respond(200, {}, '<a id="a' + i + '" hx-get="/test2" hx-swap="innerHTML">' + i + '</a>');
xhr.respond(200, {}, '<a id="a' + i + '" kt-get="/test2" kt-swap="innerHTML">' + i + '</a>');
});
this.server.respondWith("GET", "/test2", "*");
var div = make('<div hx-get="/test" hx-swap="append">*</div>')
var div = make('<div kt-get="/test" kt-swap="append">*</div>')
div.click();
this.server.respond();
div.innerText.should.equal("*1");
@@ -195,11 +195,11 @@ describe("HTMx AJAX Tests", function(){
var i = 0;
this.server.respondWith("GET", "/test", function(xhr){
i++;
xhr.respond(200, {}, '<a id="a' + i + '" hx-get="/test2" hx-swap="innerHTML">' + i + '</a>');
xhr.respond(200, {}, '<a id="a' + i + '" kt-get="/test2" kt-swap="innerHTML">' + i + '</a>');
});
this.server.respondWith("GET", "/test2", "*");
var div = make('<div hx-get="/test" hx-swap="append"></div>')
var div = make('<div kt-get="/test" kt-swap="append"></div>')
div.click();
this.server.respond();
div.innerText.should.equal("1");
@@ -217,11 +217,11 @@ describe("HTMx AJAX Tests", function(){
div.innerText.should.equal("**");
});
it('handles hx-target properly', function()
it('handles kt-target properly', function()
{
this.server.respondWith("GET", "/test", "Clicked!");
var btn = make('<button hx-get="/test" hx-target="#s1">Click Me!</button>');
var btn = make('<button kt-get="/test" kt-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("HTMx AJAX Tests", function(){
{
this.server.respondWith("GET", "/test", [204, {}, "No Content!"]);
var btn = make('<button hx-get="/test">Click Me!</button>');
var btn = make('<button kt-get="/test">Click Me!</button>');
btn.click();
btn.innerHTML.should.equal("Click Me!");
this.server.respond();
btn.innerHTML.should.equal("Click Me!");
});
it('handles hx-trigger with non-default value', function()
it('handles kt-trigger with non-default value', function()
{
this.server.respondWith("GET", "/test", "Focused!");
var btn = make('<button hx-get="/test" hx-trigger="focus">Focus Me!</button>');
var btn = make('<button kt-get="/test" kt-trigger="focus">Focus Me!</button>');
btn.focus();
btn.innerHTML.should.equal("Focus Me!");
this.server.respond();
btn.innerHTML.should.equal("Focused!");
});
it('handles hx-trigger with load event', function()
it('handles kt-trigger with load event', function()
{
this.server.respondWith("GET", "/test", "Loaded!");
var div = make('<div hx-get="/test" hx-trigger="load">Load Me!</div>');
var div = make('<div kt-get="/test" kt-trigger="load">Load Me!</div>');
div.innerHTML.should.equal("Load Me!");
this.server.respond();
div.innerHTML.should.equal("Loaded!");
@@ -266,7 +266,7 @@ describe("HTMx AJAX Tests", function(){
xhr.overriddenMimeType.should.equal("text/html");
done();
});
var div = make('<div hx-get="/test">Click Me!</div>');
var div = make('<div kt-get="/test">Click Me!</div>');
div.click();
this.server.respond();
});
@@ -278,28 +278,28 @@ describe("HTMx AJAX Tests", function(){
xhr.respond(200, {}, "click " + i);
i++
});
var div = make('<div hx-get="/test"></div>');
var div = make('<div kt-get="/test"></div>');
div.click();
div.click();
this.server.respond();
div.innerHTML.should.equal("click 1");
});
it('properly handles hx-select for basic situation', function()
it('properly handles kt-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 hx-get="/test" hx-select="#d1"></div>');
var div = make('<div kt-get="/test" kt-select="#d1"></div>');
div.click();
this.server.respond();
div.innerHTML.should.equal("<div id=\"d1\">foo</div>");
});
it('properly handles hx-select for full html document situation', function()
it('properly handles kt-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 hx-get="/test" hx-select="#d1"></div>');
var div = make('<div kt-get="/test" kt-select="#d1"></div>');
div.click();
this.server.respond();
div.innerHTML.should.equal("<div id=\"d1\">foo</div>");

View File

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

33
test/browser-only.html Normal file
View File

@@ -0,0 +1,33 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Mocha Tests</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="../node_modules/mocha/mocha.css" />
</head>
<body>
<div id="mocha"></div>
<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 class="mocha-init">
mocha.setup('bdd');
mocha.checkLeaks();
should = chai.should();
</script>
<script src="util.js"></script>
<script src="history.js"></script>
<script src="boost.js"></script>
<script class="mocha-exec">
mocha.run();
</script>
<em>Work Area</em>
<hr/>
<div id="work-area" class="kt-history-element">
</div>
</body>
</html>

View File

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

View File

@@ -1,4 +1,4 @@
describe("HTMx AJAX Headers Tests", function() {
describe("kutty AJAX Headers Tests", function() {
beforeEach(function () {
this.server = makeServer();
clearWorkArea();
@@ -8,50 +8,50 @@ describe("HTMx AJAX Headers Tests", function() {
clearWorkArea();
});
it("should include the X-HX-Request header", function(){
it("should include the X-KT-Request header", function(){
this.server.respondWith("GET", "/test", function(xhr){
xhr.requestHeaders['X-HX-Request'].should.be.equal('true');
xhr.requestHeaders['X-KT-Request'].should.be.equal('true');
xhr.respond(200, {}, "");
});
var div = make('<div hx-get="/test"></div>');
var div = make('<div kt-get="/test"></div>');
div.click();
this.server.respond();
})
it("should include the X-HX-Trigger-Id header", function(){
it("should include the X-KT-Trigger-Id header", function(){
this.server.respondWith("GET", "/test", function(xhr){
xhr.requestHeaders['X-HX-Trigger-Id'].should.equal('d1');
xhr.requestHeaders['X-KT-Trigger-Id'].should.equal('d1');
xhr.respond(200, {}, "");
});
var div = make('<div id="d1" hx-get="/test"></div>');
var div = make('<div id="d1" kt-get="/test"></div>');
div.click();
this.server.respond();
})
it("should include the X-HX-Trigger-Name header", function(){
it("should include the X-KT-Trigger-Name header", function(){
this.server.respondWith("GET", "/test", function(xhr){
xhr.requestHeaders['X-HX-Trigger-Name'].should.equal('n1');
xhr.requestHeaders['X-KT-Trigger-Name'].should.equal('n1');
xhr.respond(200, {}, "");
});
var div = make('<button name="n1" hx-get="/test"></button>');
var div = make('<button name="n1" kt-get="/test"></button>');
div.click();
this.server.respond();
})
it("should include the X-HX-Target-Id header", function(){
it("should include the X-KT-Target-Id header", function(){
this.server.respondWith("GET", "/test", function(xhr){
xhr.requestHeaders['X-HX-Target-Id'].should.equal('d1');
xhr.requestHeaders['X-KT-Target-Id'].should.equal('d1');
xhr.respond(200, {}, "");
});
var div = make('<div hx-target="#d1" hx-get="/test"></div><div id="d1" ></div>');
var div = make('<div kt-target="#d1" kt-get="/test"></div><div id="d1" ></div>');
div.click();
this.server.respond();
})
it("should handle simple string X-HX-Trigger response header properly", function(){
this.server.respondWith("GET", "/test", [200, {"X-HX-Trigger" : "foo"}, ""]);
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 hx-get="/test"></div>');
var div = make('<div kt-get="/test"></div>');
var invokedEvent = false;
div.addEventListener("foo", function (evt) {
invokedEvent = true;
@@ -61,10 +61,10 @@ describe("HTMx AJAX Headers Tests", function() {
invokedEvent.should.equal(true);
})
it("should handle basic JSON X-HX-Trigger response header properly", function(){
this.server.respondWith("GET", "/test", [200, {"X-HX-Trigger" : "{\"foo\":null}"}, ""]);
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 hx-get="/test"></div>');
var div = make('<div kt-get="/test"></div>');
var invokedEvent = false;
div.addEventListener("foo", function (evt) {
invokedEvent = true;
@@ -76,10 +76,10 @@ describe("HTMx AJAX Headers Tests", function() {
invokedEvent.should.equal(true);
})
it("should handle JSON with array arg X-HX-Trigger response header properly", function(){
this.server.respondWith("GET", "/test", [200, {"X-HX-Trigger" : "{\"foo\":[1, 2, 3]}"}, ""]);
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 hx-get="/test"></div>');
var div = make('<div kt-get="/test"></div>');
var invokedEvent = false;
div.addEventListener("foo", function (evt) {
invokedEvent = true;
@@ -91,10 +91,10 @@ describe("HTMx AJAX Headers Tests", function() {
invokedEvent.should.equal(true);
})
it("should handle JSON with array arg X-HX-Trigger response header properly", function(){
this.server.respondWith("GET", "/test", [200, {"X-HX-Trigger" : "{\"foo\":{\"a\":1, \"b\":2}}"}, ""]);
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 hx-get="/test"></div>');
var div = make('<div kt-get="/test"></div>');
var invokedEvent = false;
div.addEventListener("foo", function (evt) {
invokedEvent = true;

View File

@@ -1,4 +1,4 @@
describe("HTMx History Tests", function() {
describe("kutty History Tests", function() {
beforeEach(function () {
this.server = makeServer();
@@ -13,7 +13,7 @@ describe("HTMx History Tests", function() {
this.server.respondWith("GET", "/test", "second");
getWorkArea().innerHTML.should.be.equal("");
var div = make('<div hx-push-url="true" hx-get="/test">first</div>');
var div = make('<div kt-push-url="true" kt-get="/test">first</div>');
div.click();
this.server.respond();
getWorkArea().textContent.should.equal("second")
@@ -32,7 +32,7 @@ describe("HTMx History Tests", function() {
});
getWorkArea().innerHTML.should.equal("");
var div = make('<div hx-push-url="true" hx-get="/test" class="">0</div>');
var div = make('<div kt-push-url="true" kt-get="/test" class="">0</div>');
div.click();
this.server.respond();
getWorkArea().textContent.should.equal("1")
@@ -56,7 +56,7 @@ describe("HTMx History Tests", function() {
this.server.respondWith("GET", "/test", "second");
getWorkArea().innerHTML.should.equal("");
var div = make('<div hx-push-url="true" hx-get="/test" class="">first</div>');
var div = make('<div kt-push-url="true" kt-get="/test" class="">first</div>');
div.click();
this.server.respond();
getWorkArea().textContent.should.equal("second")

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/htmx.js"></script>
<script src="../src/kutty.js"></script>
<script class="mocha-init">
mocha.setup('bdd');
mocha.checkLeaks();
@@ -31,12 +31,15 @@
<!--<script src="history.js"></script>-->
<!--<script src="boost.js"></script>-->
<a href="browser-only.html">Run Browser-Only Tests</a>
<script class="mocha-exec">
mocha.run();
</script>
<em>Work Area</em>
<hr/>
<div id="work-area" class="hx-history-element">
<div id="work-area" class="kt-history-element">
</div>
</body>
</html>

View File

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

View File

@@ -1,4 +1,4 @@
describe("HTMx Direct Swap", function () {
describe("kutty Direct Swap", function () {
beforeEach(function () {
this.server = makeServer();
clearWorkArea();
@@ -9,8 +9,8 @@ describe("HTMx Direct Swap", function () {
});
it('handles basic response properly', function () {
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>');
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>');
make('<div id="d1"></div>');
div.click();
this.server.respond();
@@ -19,8 +19,8 @@ describe("HTMx Direct Swap", function () {
})
it('handles no id match properly', function () {
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>');
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>');
div.click();
this.server.respond();
div.innerText.should.equal("Clicked");

View File

@@ -9,7 +9,7 @@
opacity: 0;
}
.hx-show-indicator .indicator {
.kt-show-indicator .indicator {
opacity: 100%;
}
@@ -19,18 +19,18 @@
</head>
<body>
<script src="../node_modules/sinon/pkg/sinon.js"></script>
<script src="../src/htmx.js"></script>
<script src="../src/kutty.js"></script>
<script src="util.js"></script>
<script src="scratch_server.js"></script>
<script>
// this.server.respondWith("GET", "/test", '<a hx-get="/test2">Click Me</a>');
// this.server.respondWith("GET", "/test", '<a kt-get="/test2">Click Me</a>');
// this.server.respondWith("GET", "/test2", "Clicked!");
//
// make('<div hx-get="/test">dd</div>')
// make('<div kt-get="/test">dd</div>')
this.server.respondWith("GET", "/test", '<div id="d1" style="color: red; margin: 100px">Foo</div>');
make('<div hx-swap="outerHTML" hx-get="/test" hx-push-url="true" id="d1">Foo</div>');
make('<div kt-swap="outerHTML" kt-get="/test" kt-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" class="hx-history-element">
<div id="work-area" class="kt-history-element">
</div>

View File

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

View File

@@ -1,6 +1,6 @@
/* Test Utilities */
HTMx.logger = function(elt, event, data) {
kutty.logger = function(elt, event, data) {
if(console) {
console.log(event, elt, data);
}
@@ -17,7 +17,7 @@ function make(htmlStr) {
var wa = getWorkArea();
for (var i = fragment.children.length - 1; i >= 0; i--) {
var child = fragment.children[i];
HTMx.processElement(child);
kutty.processElement(child);
wa.appendChild(child);
}
return wa.lastChild;

View File

@@ -1,4 +1,4 @@
describe("HTMx Value Handling", function() {
describe("kutty Value Handling", function() {
beforeEach(function () {
this.server = makeServer();
clearWorkArea();
@@ -10,69 +10,69 @@ describe("HTMx Value Handling", function() {
it('Input includes value', function () {
var input = make('<input name="foo" value="bar"/>');
var vals = HTMx._('getInputValues')(input);
var vals = kutty._('getInputValues')(input);
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 = HTMx._('getInputValues')(input);
var vals = kutty._('getInputValues')(input);
vals['foo'].should.equal('bar');
vals['do'].should.equal('rey');
})
it('Basic form works', function () {
var form = make('<form><input id="i1" name="foo" value="bar"/><input id="i2" name="do" value="rey"/></form>');
var vals = HTMx._('getInputValues')(form);
var vals = kutty._('getInputValues')(form);
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 = HTMx._('getInputValues')(form);
var vals = kutty._('getInputValues')(form);
vals['foo'].should.equal('bar');
vals['do'].should.deep.equal(['rey', 'rey']);
})
it('hx-include works with form', function () {
it('kt-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 hx-include="#f1"></div>');
var vals = HTMx._('getInputValues')(div);
var div = make('<div kt-include="#f1"></div>');
var vals = kutty._('getInputValues')(div);
vals['foo'].should.equal('bar');
vals['do'].should.deep.equal(['rey', 'rey']);
})
it('hx-include works with input', function () {
it('kt-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 hx-include="#i1"></div>');
var vals = HTMx._('getInputValues')(div);
var div = make('<div kt-include="#i1"></div>');
var vals = kutty._('getInputValues')(div);
vals['foo'].should.equal('bar');
should.equal(vals['do'], undefined);
})
it('hx-include works with two inputs', function () {
it('kt-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 hx-include="#i1, #i2"></div>');
var vals = HTMx._('getInputValues')(div);
var div = make('<div kt-include="#i1, #i2"></div>');
var vals = kutty._('getInputValues')(div);
vals['foo'].should.equal('bar');
vals['do'].should.deep.equal(['rey', 'rey']);
})
it('hx-include works with two inputs, plus form', function () {
it('kt-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 hx-include="#i1, #i2, #f1"></div>');
var vals = HTMx._('getInputValues')(div);
var div = make('<div kt-include="#i1, #i2, #f1"></div>');
var vals = kutty._('getInputValues')(div);
vals['foo'].should.equal('bar');
vals['do'].should.deep.equal(['rey', 'rey']);
})
it('correctly URL escapes values', function () {
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");
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");
});
});

View File

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

View File

@@ -1,8 +1,11 @@
const pluginSyntaxHighlight = require("@11ty/eleventy-plugin-syntaxhighlight");
const pluginSass = require("eleventy-plugin-sass");
module.exports = function(config) {
config.addPlugin(pluginSyntaxHighlight);
config.addPassthroughCopy("js");
config.addPassthroughCopy("css");
config.addPassthroughCopy("img");
config.addPassthroughCopy("_");
}
config.addPlugin(pluginSass, {});
}

View File

@@ -0,0 +1,52 @@
<html lang="en">
<head>
<title>kutty - 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-theme.css"/>
<script src="/js/htmx.js"></script>
<script>
kutty.logger = function(elt, event, data) {
if(console) {
console.log(event, elt, data);
}
}
</script>
</head>
<body>
<div class="top-nav">
<div class="c">
<div class="row">
<div class="2 col">
<span class="logo light" kt-add-class="settle">&lt;<a>/</a>&gt; k<a>u</a>tty</span>
</div>
<div class="10 col nav">
<div class="row">
<div class="1 col">
<a href="/">home</a>
</div>
<div class="1 col">
<a href="/docs">docs</a>
</div>
<div class="1 col">
<a href="/docs/attributes">attributes</a>
</div>
<div class="1 col">
<a href="/docs/attributes">events</a>
</div>
<div class="1 col">
<a href="/docs/attributes">headers</a>
</div>
<div class="1 col">
<a href="https://github.com/bigskysoftware/kutty">github</a>
</div>
</div>
</div>
</div>
</div>
</div>
{{ content | safe }}
</body>
</html>

View File

@@ -1,52 +1,6 @@
<html lang="en">
<head>
<title>HTMx - teaching good ol' HTML some new tricks</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/css/site.css"/>
<link rel="stylesheet" href="/css/prism-theme.css"/>
<script src="/js/htmx.js"></script>
<script>
HTMx.logger = function(elt, event, data) {
if(console) {
console.log(event, elt, data);
}
}
</script>
</head>
<body>
<div class="c">
<div class="{% if page.url == '/' %}root{% endif %} top-nav">
<h1 class="hero" hx-add-class="settle">&lt;<a>/</a>&gt; HTM<sub><a>x</a></sub>
<svg onclick="document.getElementById('main-nav').classList.toggle('show')" class="hamburger" viewBox="0 0 100 80" width="30" height="30" style="margin-bottom:-8px">
<rect width="100" height="20" style="fill:rgb(52, 101, 164)" rx="10"></rect>
<rect y="30" width="100" height="20" style="fill:rgb(52, 101, 164)" rx="10"></rect>
<rect y="60" width="100" height="20" style="fill:rgb(52, 101, 164)" rx="10"></rect>
</svg>
</h1>
<div id="main-nav" class="row center nav collapse" hx-boost="true">
<div class="1 col">
<a href="/">home</a>
</div>
<div class="1 col">
<a href="/docs">docs</a>
</div>
<div class="1 col">
<a href="/docs/attributes">attributes</a>
</div>
<div class="1 col">
<a href="/docs/attributes">events</a>
</div>
<div class="1 col">
<a href="/docs/attributes">headers</a>
</div>
<div class="1 col">
<a href="https://github.com/bigskysoftware/htmx">github</a>
</div>
</div>
</div>
<div id="content">
{{ content | safe }}
</div>
---
layout: core_layout.njk
---
<div class="content c">
{{ content | safe }}
</div>
</body>
</html>

View File

@@ -1,19 +1,47 @@
$mainBlue: #3465a4;
$lightBlue: #3d72d7;
body {
margin: 0px;
line-height: 1.6;
line-height: 1.4em;
font-size: 18px;
color: #333;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif !important;
}
h2 {
border-bottom: 2px solid whitesmoke;
color: rgb(52, 101, 164);
.logo {
font-size: 28px;
font-weight: bold;
}
#content {
margin-top: 32px
.light {
}
.top-nav {
line-height: 30px;
border-bottom: 1px solid whitesmoke;
margin: 0px;
}
.dark-hero {
background-image: linear-gradient(#1f1f1f, #2d2d2d);
height: 240px;
line-height: 240px;
margin: 0;
text-align: center;
vertical-align: center;
color: whitesmoke;
.logo {
font-size: 100px;
}
a {
color: $lightBlue;
}
box-shadow:
inset 0px 11px 8px -10px #262626,
inset 0px -11px 8px -10px #262626;
border-bottom: 1px solid whitesmoke;
}
p {
@@ -43,22 +71,28 @@ blockquote {
a {
text-decoration: none;
color:rgb(52, 101, 164)
color: $mainBlue
}
.center {
text-align: center;
}
.nav ul {
list-style: none;
padding-left: 12px;
.nav {
ul {
list-style: none;
padding-left: 12px;
}
}
.hamburger {
display: none;
}
h2:not(:first-of-type) {
margin-top: 16px;
}
/* customized version of lit.css */
* + *{
box-sizing: border-box;
@@ -110,7 +144,7 @@ a {
.card:focus {
outline: 0;
border: solid rgb(52, 101, 164);
border: solid $mainBlue;
}
hr {
@@ -134,8 +168,8 @@ a[href]:hover, .btn:hover {
.btn.primary {
color: white;
background: rgb(52, 101, 164);
border: solid rgb(52, 101, 164);
background: $mainBlue;
border: solid $mainBlue;
}
td {

View File

@@ -34,12 +34,12 @@ title: HTMx - HTML Extensions
</div>
<div class="10 col">
## <a name="introduction"></a>[HTMx in a Nutshell](#introduction)
## <a name="introduction"></a>[Kutty in a Nutshell](#introduction)
HTMx is a set of attributes that allow you to access modern browser features directly from HTML, rather than using
Kutty is a library that allows you to access modern browser features directly from HTML, rather than using
javascript.
To understand how HTMx works, first lets take a look at an anchor tag:
To understand kutty, first lets take a look at an anchor tag:
``` html
<a href="/blog">Blog</a>
@@ -50,59 +50,60 @@ This anchor tag tells a browser:
> "When a user clicks on this link, issue an HTTP GET request to '/blog' and load the response content
> into the browser window".
With that in mind, consider the following HTMx code:
With that in mind, consider the following bit of HTML:
``` html
<div hx-post="/clicked">Click Me!</div>
<div kt-post="/clicked">Click Me!</div>
```
This tells a browser:
This tells kutty:
> "When a user clicks on this div, issue an HTTP POST request to '/clicked' and load the response content into the inner
> html of this element"
So the difference is that with HTMx:
So kutty extends the basic idea of that anchor tag:
* Any element can issue a HTTP request
* Any event can trigger the request (not just clicks or form submissions)
* The HTTP request is done via AJAX
* Different HTTP verbs can used
* The response replaces the content of the element, rather than the entire page
HTMx expects responses to the AJAX calls that it makes to be *HTML* rather than *JSON*, as is more typical with AJAX
requests.
When you are using kutty, you respond to the AJAX calls with *HTML* rather than *JSON*, often a small amount of
HTML rather than the whole page.
If you prefer it, you can use the `data-` prefix when using HTMx:
If you prefer, you can use the `data-` prefix when using kutty:
``` html
<a data-hx-post="/click">Click Me!</a>
<a data-kt-post="/click">Click Me!</a>
```
## <a name="installing"></a> [Installing](#installing)
HTMx is a dependency-free javascript library.
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/):
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/):
``` html
<script src="https://unpkg.com/htmx.org@0.0.1"></script>
<script src="https://unpkg.com/kutty.org@0.0.1"></script>
```
## <a name="ajax"></a> [AJAX](#ajax)
One of the primary features HTMx provides are attributes to allow you to issue AJAX requests directly from HTML:
* [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 (see [details](#htmx-request-details))
* [hx-patch](/attributes/hx-patch) - Issues a `PATCH` request to the given URL (see [details](#htmx-request-details))
* [hx-delete](/attributes/hx-delete) - Issues a `GET` request to the given URL (see [details](#htmx-request-details))
* [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 (see [details](#htmx-request-details))
* [kt-patch](/attributes/kt-patch) - Issues a `PATCH` request to the given URL (see [details](#htmx-request-details))
* [kt-delete](/attributes/kt-delete) - Issues a `GET` request to the given URL (see [details](#htmx-request-details))
Each of these attributes takes a URL to issue an AJAX request to. The element will issue a request of the specified
type to the given URL when the element is [triggered](#triggers):
```html
<div hx-put="/messages">Put To Messages</div>
<div kt-put="/messages">Put To Messages</div>
```
This tells the browser:
@@ -117,40 +118,40 @@ By default AJAX requests are triggered by the "natural" event of an element:
* `form`: the `submit` event
* everything else: the `click` event
If you don't want the request to happen on the default event, you can use the [hx-trigger](/attributes/hx-trigger)
If you don't want the request to happen on the default event, you can use the [kt-trigger](/attributes/kt-trigger)
attribute to specify the event of interest. Here is a `div` that posts to `/mouse_entered`
when a mouse enters it:
```html
<div hx-post="/mouse_entered" hx-trigger="mouseenter">
<div kt-post="/mouse_entered" kt-trigger="mouseenter">
[Here Mouse, Mouse!]
</div>
```
If you want a request to only happen once, you can use the [hx-trigger-once](/attributes/hx-trigger-once) attribute:
If you want a request to only happen once, you can use the [kt-trigger-once](/attributes/kt-trigger-once) attribute:
```html
<div hx-post="/mouse_entered" hx-trigger="mouseenter"
hx-trigger-once="true">
<div kt-post="/mouse_entered" kt-trigger="mouseenter"
kt-trigger-once="true">
[Here Mouse, Mouse!]
</div>
```
There are two additional modifiers you can use for trigger:
* [hx-trigger-changed-only](/attributes/hx-trigger-changed-only) - when set to `true` the element will only issue a
* [kt-trigger-changed-only](/attributes/kt-trigger-changed-only) - when set to `true` the element will only issue a
request if its value has changed
* [hx-trigger-delay](/attributes/hx-trigger-delay) - tells HTMx to wait the given amount of time (e.g. `1s`) before
* [kt-trigger-delay](/attributes/kt-trigger-delay) - tells HTMx to wait the given amount of time (e.g. `1s`) before
issuing the request. If the event triggers again, the countdown is reset.
You can use these two attributes to implement a common UX pattern, [Live Search](/demo/live-search):
```html
<input type="text" name="q"
hx-get="/trigger_delay"
hx-trigger="keyup"
hx-target="#search-results"
hx-trigger-delay="500ms" placeholder="Search..."/>
kt-get="/trigger_delay"
kt-trigger="keyup"
kt-target="#search-results"
kt-trigger-delay="500ms" placeholder="Search..."/>
<div id="search-results"></div>
```
@@ -159,7 +160,7 @@ into the `div#search-results`.
#### <a name="special-events"></a> [Special Events](#special-events)
HTMx provides a few special events for use in [hx-trigger](/attributes/hx-trigger):
HTMx provides a few special events for use in [kt-trigger](/attributes/kt-trigger):
* `load` - fires once when the element is first loaded
* `revealed` - fires once when an element first scrolls into the viewport
@@ -171,7 +172,7 @@ You can also use custom events to trigger requests if you have an advanced use c
If you want an element to poll the given URL rather than wait for an event, you can use the `every` syntax:
```html
<div hx-get="/news" trigger="every 2s"></div>
<div kt-get="/news" trigger="every 2s"></div>
```
This tells HTMx
@@ -186,7 +187,7 @@ server and the browser than websockets.
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 [hx-sse-src](/attributes/hx-sse-src) attribute on a parent element
1. Define an SSE source. To do this, add a [kt-sse-src](/attributes/kt-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:`
@@ -194,8 +195,8 @@ that specifies the URL from which Server Sent Events will be received.
Here is an example:
```html
<body hx-sse-src="/sse_messages">
<div trigger="sse:new_news" hx-get="/news"></div>
<body kt-sse-src="/sse_messages">
<div trigger="sse:new_news" kt-get="/news"></div>
</body>
```
@@ -205,33 +206,33 @@ 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 HTMx by using the [hx-indicator](/attributes/hx-indicator)
attribute, the `hx-show-indicator` class and some CSS.
will not give them any feedback. You can accomplish this in HTMx by using the [kt-indicator](/attributes/kt-indicator)
attribute, the `kutty-show-indicator` class and some CSS.
By default the `hx-show-indicator` class will be put on the element issuing the request. This can be used to show a
By default the `kutty-show-indicator` class will be put on the element issuing the request. This can be used to show a
spinner gif, for example:
```html
<style>
.indicator { display: none }
.hx-show-indicator .indicator { display: inline }
.kutty-show-indicator .indicator { display: inline }
</style>
<button hx-get="/click">
<button kt-get="/click">
Click Me!
<img class="indicator" src="/spinner.gif"/>
</button>
```
If you want the `hx-show-indicator` class added to a different element, you can use the [hx-indicator](/attributes/hx-indicator)
If you want the `kutty-show-indicator` class added to a different element, you can use the [kt-indicator](/attributes/kt-indicator)
attribute with a CSS selector to do so:
```html
<style>
.indicator { display: none }
.hx-show-indicator .indicator { display: inline }
.kutty-show-indicator .indicator { display: inline }
</style>
<div id="parent-div">
<button hx-get="/click" hx-indicator="#parent-div">
<button kt-get="/click" kt-indicator="#parent-div">
Click Me!
</button>
<img class="indicator" src="/spinner.gif"/>
@@ -240,14 +241,14 @@ attribute with a CSS selector to do so:
### <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 [hx-target](/attributes/hx-target) attribute, which takes a CSS selector. Looking back at our Live Search example:
use the [kt-target](/attributes/kt-target) attribute, which takes a CSS selector. Looking back at our Live Search example:
```html
<input type="text" name="q"
hx-get="/trigger_delay"
hx-trigger="keyup"
hx-target="#search-results"
hx-trigger-delay="500ms" placeholder="Search..."/>
kt-get="/trigger_delay"
kt-trigger="keyup"
kt-target="#search-results"
kt-trigger-delay="500ms" placeholder="Search..."/>
<div id="search-results"></div>
```
@@ -257,7 +258,7 @@ input tag.
### <a name="swapping"></a> [Swapping](#swapping)
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
`innerHTML` of the target element. You can modify this by using the [kt-swap](/attributes/kt-swap) attribute
with any of the following values:
* `innerHTML` - the default, puts the content inside the target element
@@ -270,10 +271,10 @@ with any of the following values:
#### Out of Band Swaps
If you want to swap content from a response directly into the DOM by using the `id` attribute you can use the
[hx-swap-oob](/attributes/hx-swap-oob) attribute in the *response* html:
[kt-swap-oob](/attributes/kt-swap-oob) attribute in the *response* html:
```html
<div id="message" hx-swap-oob="true">Swap me directly!</div>
<div id="message" kt-swap-oob="true">Swap me directly!</div>
Additional Content
```
@@ -286,7 +287,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 [hx-select](/attributes/hx-select)
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)
attribute, which takes a CSS selector and selects the matching elements from the response.
### <a name="forms"></a> [Forms & Input Values](#forms)
@@ -294,7 +295,7 @@ attribute, which takes a CSS selector and selects the matching elements from the
By default, an element will include its value if it has one. Additionally, if the element is in a form, all values
in the form will be included in the request.
If you wish to include the values of other elements, you can use the [hx-include](/attributes/hx-include) attribute
If you wish to include the values of other elements, you can use the [kt-include](/attributes/kt-include) attribute
with a CSS selector of all the elements whose values you want to include in the request.
Finally, if you want to programatically modify the arguments, you can use the [values.hx](/events/values.hx) event to
@@ -305,10 +306,10 @@ do so.
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 into the browser navigation bar and add the current state of the page
to the browser's history, include the [hx-push](/attributes/hx-push) attribute:
to the browser's history, include the [kt-push](/attributes/kt-push) attribute:
```html
<a hx-get="/Blog" hx-push="true">Blog</a>
<a kt-get="/Blog" kt-push="true">Blog</a>
```
When a user clicks on this link, HTMx will snapshot the current DOM and store it before it makes a request to /blog.
@@ -320,7 +321,7 @@ When a user hits the back button, HTMx will retrieve the old content from storag
### Specifying History Snapshot 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)
if you want to use a narrower element for snapshotting you can use the [kt-history-element](/attributes/kt-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.
@@ -331,22 +332,22 @@ Careful: this element will need to be on all pages or restoring from history won
HTMx includes a number of useful headers in requests:
* `X-HX-Request` - will be set to "true"
* `X-HX-Trigger-Id` - 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-Id` - 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-Value` - the value of the current active element
* `X-KT-Request` - will be set to "true"
* `X-KT-Trigger-Id` - 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-Id` - 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-Value` - the value of the current active element
### Response Headers
HTMx supports two special response headers:
* `X-HX-Trigger` - can be used to trigger client side events, see the [documentation](/events/x-hx-trigger) for examples.
* `X-HX-Push` - can be used to push a new URL into the browsers address bar
* `X-KT-Trigger` - can be used to trigger client side events, see the [documentation](/events/X-KT-trigger) for examples.
* `X-KT-Push` - can be used to push a new URL into the browsers address bar
### Request Order of Operations
@@ -354,9 +355,9 @@ The order of operations in a HTMx request are:
* The element is triggered and begins a request
* Values are gathered for the request
* The `hx-show-indicator` class is applied to the appropriate elements
* The `kutty-show-indicator` 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 `hx-swapping` class
* Upon getting a response the target element is marked with the `kutty-swapping` class
* An optional swap delay is done (default: no delay)
* The actual content swap is done
* A settle delay is done (default: 100ms)

View File

@@ -13,15 +13,15 @@ title: HTMx - HTML Extensions / Attributes
</thead>
<tbody>
<tr>
<td>hx-get</td>
<td>kt-get</td>
<td>Issues an HTTP GET to the given URL</td>
</tr>
<tr>
<td>hx-target</td>
<td>kt-target</td>
<td>Specifies the target element that should be swapped</td>
</tr>
<tr>
<td>hx-swap</td>
<td>kt-swap</td>
<td>Specifies how target element should be swapped: innerHTML, outerHTML, append</td>
</tr>
</tbody>

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -1,29 +1,37 @@
---
layout: layout.njk
layout: core_layout.njk
title: HTMx - HTML Extensions
---
<div class="dark-hero">
<span class="logo dark" kt-add-class="settle">&lt;<a>/</a>&gt; k<a>u</a>tty</span>
<sub><i>high powered tools for HTML</i></sub>
</div>
<div class="c">
## Introduction
HTMx is a small (<12Kb) &amp; dependency-free library that surfaces the features of modern browsers using HTML
attributes. Using HTMx you can implement many [UX patterns](/demo) that would typically require writing javascript.
Kutty is a set of HTML extensions (attributes, request headers, etc.) that allow you to use markup to build
[powerful UX](/demo), while keeping the simplicity of the hypertext we all know and love.
HTMx is unobtrusive, plays well with other tools, can be adopted incrementally with no up-front rewrites.
Kutty is a small (<6Kb min.gz'd), dependency-free, and can be adopted incrementally without a huge rewrite.
## Quick Start
``` html
<!-- Load from unpkg -->
<script src="https://unpkg.com/htmx.org@0.0.1"></script>
<!-- enhance a button -->
<button hx-get="/example">Click Me</button>
<script src="https://unpkg.com/kutty.org@0.0.1"></script>
<!-- have a button POST a click via AJAX -->
<button kt-post="/clicked" kt-swap="outerHTML">Click Me</button>
```
This code tells HTMx that:
This annotation tells kutty:
> "When a user clicks on this button, issue an AJAX request to /example, and load the content into the body
> of the button"
> "When a user clicks on this button, issue an AJAX request to /example, and replace the button with the response"
HTMx is based on [intercooler.js](http://intercoolerjs.org) and is the successor to that project.
Kutty is based on [intercooler.js](http://intercoolerjs.org) and is the successor to that project.
</div>

View File

@@ -1,5 +1,5 @@
// noinspection JSUnusedAssignment
var HTMx = HTMx || (function () {
var kutty = kutty || (function () {
'use strict';
var VERBS = ['get', 'post', 'put', 'delete', 'patch']
@@ -24,7 +24,7 @@ var HTMx = HTMx || (function () {
return elt.getAttribute && elt.getAttribute(name);
}
// resolve with both hx and data-hx prefixes
// resolve with both kt and data-kt prefixes
function getAttributeValue(elt, qualifiedName) {
return getRawAttribute(elt, qualifiedName) || getRawAttribute(elt, "data-" + qualifiedName);
}
@@ -87,7 +87,7 @@ var HTMx = HTMx || (function () {
}
function getInternalData(elt) {
var dataProp = 'hx-data-internal';
var dataProp = 'kutty-internal-data';
var data = elt[dataProp];
if (!data) {
data = elt[dataProp] = {};
@@ -129,9 +129,9 @@ var HTMx = HTMx || (function () {
//====================================================================
function getTarget(elt) {
var explicitTarget = getClosestMatch(elt, function(e){return getRawAttribute(e,"hx-target") !== null});
var explicitTarget = getClosestMatch(elt, function(e){return getRawAttribute(e,"kt-target") !== null});
if (explicitTarget) {
var targetStr = getRawAttribute(explicitTarget, "hx-target");
var targetStr = getRawAttribute(explicitTarget, "kt-target");
if (targetStr === "this") {
return explicitTarget;
} else {
@@ -161,7 +161,7 @@ var HTMx = HTMx || (function () {
function handleOutOfBandSwaps(fragment) {
var settleTasks = [];
forEach(fragment.children, function(child){
if (getAttributeValue(child, "hx-swap-oob") === "true") {
if (getAttributeValue(child, "kt-swap-oob") === "true") {
var target = getDocument().getElementById(child.id);
if (target) {
var fragment = new DocumentFragment()
@@ -169,7 +169,7 @@ var HTMx = HTMx || (function () {
settleTasks = settleTasks.concat(swapOuterHTML(target, fragment));
} else {
child.parentNode.removeChild(child);
triggerEvent(getDocument().body, "oobErrorNoTarget.hx", {id:child.id, content:child})
triggerEvent(getDocument().body, "oobErrorNoTarget.kutty", {id:child.id, content:child})
}
}
})
@@ -197,7 +197,7 @@ var HTMx = HTMx || (function () {
var child = fragment.firstChild;
parentNode.insertBefore(child, insertBefore);
if (child.nodeType !== Node.TEXT_NODE) {
triggerEvent(child, 'load.hx', {elt:child, parent:parentElt(child)});
triggerEvent(child, 'load.kutty', {elt:child, parent:parentElt(child)});
processNode(child);
}
}
@@ -243,7 +243,7 @@ var HTMx = HTMx || (function () {
}
function maybeSelectFromResponse(elt, fragment) {
var selector = getClosestAttributeValue(elt, "hx-select");
var selector = getClosestAttributeValue(elt, "kt-select");
if (selector) {
var newFragment = new DocumentFragment();
forEach(fragment.querySelectorAll(selector), function (node) {
@@ -260,7 +260,7 @@ var HTMx = HTMx || (function () {
fragment = maybeSelectFromResponse(elt, fragment);
var swapStyle = getClosestAttributeValue(elt, "hx-swap");
var swapStyle = getClosestAttributeValue(elt, "kt-swap");
switch(swapStyle) {
case "outerHTML": return concat(settleTasks, swapOuterHTML(target, fragment));
case "prepend": return concat(settleTasks, swapPrepend(target, fragment));
@@ -291,7 +291,7 @@ var HTMx = HTMx || (function () {
}
function getTrigger(elt) {
var explicitTrigger = getClosestAttributeValue(elt, 'hx-trigger');
var explicitTrigger = getClosestAttributeValue(elt, 'kt-trigger');
if (explicitTrigger) {
return explicitTrigger;
} else {
@@ -337,7 +337,7 @@ var HTMx = HTMx || (function () {
nodeData.timeout = setTimeout(function () {
if (bodyContains(elt)) {
issueAjaxRequest(elt, verb, path);
processPolling(elt, verb, getAttributeValue(elt, "hx-" + verb));
processPolling(elt, verb, getAttributeValue(elt, "kt-" + verb));
}
}, interval);
}
@@ -373,14 +373,14 @@ var HTMx = HTMx || (function () {
var elementData = getInternalData(elt);
if (!eventData.handled) {
eventData.handled = true;
if (getAttributeValue(elt, "hx-trigger-once") === "true") {
if (getAttributeValue(elt, "kt-trigger-once") === "true") {
if (elementData.triggeredOnce) {
return;
} else {
elementData.triggeredOnce = true;
}
}
if (getAttributeValue(elt, "hx-trigger-changed-only") === "true") {
if (getAttributeValue(elt, "kt-trigger-changed-only") === "true") {
if (elementData.lastValue === elt.value) {
return;
} else {
@@ -390,7 +390,7 @@ var HTMx = HTMx || (function () {
if (elementData.delayed) {
clearTimeout(elementData.delayed);
}
var eventDelay = getAttributeValue(elt, "hx-trigger-delay");
var eventDelay = getAttributeValue(elt, "kt-trigger-delay");
var issueRequest = function(){
issueAjaxRequest(elt, verb, path, evt.target);
}
@@ -407,13 +407,13 @@ var HTMx = HTMx || (function () {
}
function initScrollHandler() {
if (!window['hxScrollHandler']) {
if (!window['kuttyScrollHandler']) {
var scrollHandler = function() {
forEach(getDocument().querySelectorAll("[hx-trigger='reveal']"), function (elt) {
forEach(getDocument().querySelectorAll("[kt-trigger='reveal']"), function (elt) {
maybeReveal(elt);
});
};
window['hxScrollHandler'] = scrollHandler;
window['kuttyScrollHandler'] = scrollHandler;
window.addEventListener("scroll", scrollHandler)
}
}
@@ -438,10 +438,10 @@ var HTMx = HTMx || (function () {
initializer: function() { new EventSource(sseSrc, details.config) },
config:{withCredentials: true}
};
triggerEvent(elt, "initSSE.mx", {config:details})
triggerEvent(elt, "initSSE.kutty", {config:details})
var source = details.initializer();
source.onerror = function (e) {
triggerEvent(elt, "sseError.mx", {error:e, source:source});
triggerEvent(elt, "sseError.kutty", {error:e, source:source});
maybeCloseSSESource(elt);
};
getInternalData(elt).sseSource = source;
@@ -463,7 +463,7 @@ var HTMx = HTMx || (function () {
};
sseSource.sseSource.addEventListener(sseEventName, sseListener);
} else {
triggerEvent(elt, "noSSESourceError.mx")
triggerEvent(elt, "noSSESourceError.kutty")
}
}
@@ -477,7 +477,7 @@ var HTMx = HTMx || (function () {
function processVerbs(elt, nodeData, trigger) {
var explicitAction = false;
forEach(VERBS, function (verb) {
var path = getAttributeValue(elt, 'hx-' + verb);
var path = getAttributeValue(elt, 'kt-' + verb);
if (path) {
explicitAction = true;
nodeData.path = path;
@@ -508,18 +508,18 @@ var HTMx = HTMx || (function () {
var trigger = getTrigger(elt);
var explicitAction = processVerbs(elt, nodeData, trigger);
if (!explicitAction && getClosestAttributeValue(elt, "hx-boost") === "true") {
if (!explicitAction && getClosestAttributeValue(elt, "kt-boost") === "true") {
boostElement(elt, nodeData, trigger);
}
var sseSrc = getAttributeValue(elt, 'hx-sse-source');
var sseSrc = getAttributeValue(elt, 'kt-sse-source');
if (sseSrc) {
initSSESource(elt, sseSrc);
}
var addClass = getAttributeValue(elt, 'hx-add-class');
var addClass = getAttributeValue(elt, 'kt-add-class');
if (addClass) {
processClassList(elt, addClass, "add");
}
var removeClass = getAttributeValue(elt, 'hx-remove-class');
var removeClass = getAttributeValue(elt, 'kt-remove-class');
if (removeClass) {
processClassList(elt, removeClass, "remove");
}
@@ -532,7 +532,7 @@ var HTMx = HTMx || (function () {
//====================================================================
function sendError(elt, eventName, details) {
var errorURL = getClosestAttributeValue(elt, "hx-error-url");
var errorURL = getClosestAttributeValue(elt, "kt-error-url");
if (errorURL) {
var xhr = new XMLHttpRequest();
xhr.open("POST", errorURL);
@@ -555,22 +555,22 @@ var HTMx = HTMx || (function () {
function triggerEvent(elt, eventName, details) {
details["elt"] = elt;
var event = makeEvent(eventName, details);
if (HTMx.logger) {
HTMx.logger(elt, eventName, details);
if (kutty.logger) {
kutty.logger(elt, eventName, details);
if (eventName.indexOf("Error") > 0) {
sendError(elt, eventName, details);
}
}
var eventResult = elt.dispatchEvent(event);
var allResult = elt.dispatchEvent(makeEvent("all.hx", {elt:elt, originalDetails:details, originalEvent: event}));
var allResult = elt.dispatchEvent(makeEvent("all.kutty", {elt:elt, originalDetails:details, originalEvent: event}));
return eventResult && allResult;
}
function addHTMxEventListener(arg1, arg2, arg3) {
function addKuttyEventListener(arg1, arg2, arg3) {
var target, event, listener;
if (isFunction(arg1)) {
target = getDocument().body;
event = "all.hx";
event = "all.kutty";
listener = arg1;
} else if (isFunction(arg2)) {
target = getDocument().body;
@@ -588,7 +588,7 @@ var HTMx = HTMx || (function () {
// History Support
//====================================================================
function getHistoryElement() {
var historyElt = getDocument().querySelector('.hx-history-element');
var historyElt = getDocument().querySelector('.kt-history-element');
return historyElt || getDocument().body;
}
@@ -607,21 +607,21 @@ var HTMx = HTMx || (function () {
}
function bumpHistoryAccessDate(pathAndSearch) {
var historyTimestamps = JSON.parse(localStorage.getItem("hx-history-timestamps")) || {};
var historyTimestamps = JSON.parse(localStorage.getItem("kt-history-timestamps")) || {};
historyTimestamps[pathAndSearch] = Date.now();
var paths = Object.keys(historyTimestamps);
if (paths.length > 20) {
purgeOldestPaths(paths, historyTimestamps);
}
localStorage.setItem("hx-history-timestamps", JSON.stringify(historyTimestamps));
localStorage.setItem("kt-history-timestamps", JSON.stringify(historyTimestamps));
}
function saveHistory() {
var elt = getHistoryElement();
var pathAndSearch = location.pathname+location.search;
triggerEvent(getDocument().body, "historyUpdate.hx", {path:pathAndSearch, historyElement:elt});
triggerEvent(getDocument().body, "historyUpdate.kutty", {path:pathAndSearch, historyElement:elt});
history.replaceState({}, getDocument().title, window.location.href);
localStorage.setItem('hx-history:' + pathAndSearch, elt.innerHTML);
localStorage.setItem('kt-history:' + pathAndSearch, elt.innerHTML);
bumpHistoryAccessDate(pathAndSearch);
}
@@ -636,14 +636,14 @@ var HTMx = HTMx || (function () {
}
function loadHistoryFromServer(pathAndSearch) {
triggerEvent(getDocument().body, "historyCacheMiss.hx", {path: pathAndSearch});
triggerEvent(getDocument().body, "historyCacheMiss.kutty", {path: pathAndSearch});
var request = new XMLHttpRequest();
request.open('GET', pathAndSearch, true);
request.onload = function () {
triggerEvent(getDocument().body, "historyCacheMissLoad.hx", {path: pathAndSearch});
triggerEvent(getDocument().body, "historyCacheMissLoad.kutty", {path: pathAndSearch});
if (this.status >= 200 && this.status < 400) {
var fragment = makeFragment(this.response);
fragment = fragment.querySelector('.hx-history-element') || fragment;
fragment = fragment.querySelector('.kt-history-element') || fragment;
settleImmediately(swapInnerHTML(getHistoryElement(), fragment));
}
};
@@ -651,8 +651,8 @@ var HTMx = HTMx || (function () {
function restoreHistory() {
var pathAndSearch = location.pathname+location.search;
triggerEvent(getDocument().body, "historyRestore.hx", {path:pathAndSearch});
var content = localStorage.getItem('hx-history:' + pathAndSearch);
triggerEvent(getDocument().body, "historyRestore.kutty", {path:pathAndSearch});
var content = localStorage.getItem('kt-history:' + pathAndSearch);
if (content) {
bumpHistoryAccessDate(pathAndSearch);
settleImmediately(swapInnerHTML(getHistoryElement(), makeFragment(content)));
@@ -662,7 +662,7 @@ var HTMx = HTMx || (function () {
}
function shouldPush(elt) {
return getClosestAttributeValue(elt, "hx-push-url") === "true" ||
return getClosestAttributeValue(elt, "kt-push-url") === "true" ||
(elt.tagName === "A" && getInternalData(elt).boosted);
}
@@ -675,14 +675,14 @@ var HTMx = HTMx || (function () {
}
function mutateRequestIndicatorClasses(elt, action) {
var indicator = getClosestAttributeValue(elt, 'hx-indicator');
var indicator = getClosestAttributeValue(elt, 'kt-indicator');
if (indicator) {
var indicators = getDocument().querySelectorAll(indicator);
} else {
indicators = [elt];
}
forEach(indicators, function(ic) {
ic.classList[action].call(ic.classList, "hx-show-indicator");
ic.classList[action].call(ic.classList, "kutty-show-indicator");
});
}
@@ -735,7 +735,7 @@ var HTMx = HTMx || (function () {
processInputValue(processed, values, elt);
// include any explicit includes
var includes = getAttributeValue(elt, "hx-include");
var includes = getAttributeValue(elt, "kt-include");
if (includes) {
var nodes = getDocument().querySelectorAll(includes);
forEach(nodes, function(node) {
@@ -778,7 +778,28 @@ var HTMx = HTMx || (function () {
//====================================================================
function setHeader(xhr, name, value, noPrefix) {
xhr.setRequestHeader((noPrefix ? "" : "X-HX-") + name, value || "");
xhr.setRequestHeader((noPrefix ? "" : "X-KT-") + name, value || "");
}
function setRequestHeaders(xhr, elt, target, prompt, eventTarget) {
setHeader(xhr, "Request", "true");
setHeader(xhr, "Trigger-Id", getRawAttribute(elt, "id"));
setHeader(xhr, "Trigger-Name", getRawAttribute(elt, "name"));
setHeader(xhr, "Target-Id", getRawAttribute(target, "id"));
setHeader(xhr, "Current-URL", getDocument().location.href);
if (prompt) {
setHeader(xhr, "Prompt", prompt);
}
if (eventTarget) {
setHeader(xhr, "Event-Target", getRawAttribute(eventTarget, "id"));
}
if (getDocument().activeElement) {
setHeader(xhr, "Active-Element", getRawAttribute(getDocument().activeElement, "id"));
// noinspection JSUnresolvedVariable
if (getDocument().activeElement.value) {
setHeader(xhr, "Active-Element-Value", getDocument().activeElement.value);
}
}
}
function issueAjaxRequest(elt, verb, path, eventTarget) {
@@ -792,13 +813,13 @@ var HTMx = HTMx || (function () {
eltData.requestInFlight = false
}
var target = getTarget(elt);
var promptQuestion = getClosestAttributeValue(elt, "hx-prompt");
var promptQuestion = getClosestAttributeValue(elt, "kt-prompt");
if (promptQuestion) {
var prompt = prompt(promptQuestion);
if(!triggerEvent(elt, 'prompt.hx', {prompt: prompt, target:target})) return endRequestLock();
if(!triggerEvent(elt, 'prompt.kutty', {prompt: prompt, target:target})) return endRequestLock();
}
var confirmQuestion = getClosestAttributeValue(elt, "hx-confirm");
var confirmQuestion = getClosestAttributeValue(elt, "kt-confirm");
if (confirmQuestion) {
if(!confirm(confirmQuestion)) return endRequestLock();
}
@@ -806,7 +827,7 @@ var HTMx = HTMx || (function () {
var xhr = new XMLHttpRequest();
var inputValues = getInputValues(elt);
if(!triggerEvent(elt, 'values.hx', {values: inputValues, target:target})) return endRequestLock();
if(!triggerEvent(elt, 'values.kutty', {values: inputValues, target:target})) return endRequestLock();
// request type
var requestURL;
@@ -827,31 +848,14 @@ var HTMx = HTMx || (function () {
xhr.overrideMimeType("text/html");
// request headers
setHeader(xhr, "Request", "true");
setHeader(xhr,"Trigger-Id", getRawAttribute(elt,"id"));
setHeader(xhr,"Trigger-Name", getRawAttribute(elt, "name"));
setHeader(xhr,"Target-Id", getRawAttribute(target,"id"));
setHeader(xhr,"Current-URL", getDocument().location.href);
if (prompt) {
setHeader(xhr,"Prompt", prompt);
}
if (eventTarget) {
setHeader(xhr,"Event-Target", getRawAttribute(eventTarget,"id"));
}
if (getDocument().activeElement) {
setHeader(xhr,"Active-Element", getRawAttribute(getDocument().activeElement,"id"));
// noinspection JSUnresolvedVariable
if (getDocument().activeElement.value) {
setHeader(xhr,"Active-Element-Value", getDocument().activeElement.value);
}
}
setRequestHeaders(xhr, elt, target, prompt, eventTarget);
xhr.onload = function () {
try {
if (!triggerEvent(elt, 'beforeOnLoad.hx', {xhr: xhr, target: target})) return;
if (!triggerEvent(elt, 'beforeOnLoad.kutty', {xhr: xhr, target: target})) return;
handleTrigger(elt, this.getResponseHeader("X-HX-Trigger"));
var pushedUrl = this.getResponseHeader("X-HX-Push")
handleTrigger(elt, this.getResponseHeader("X-KT-Trigger"));
var pushedUrl = this.getResponseHeader("X-KT-Push")
var shouldSaveHistory = shouldPush(elt) || pushedUrl;
@@ -860,47 +864,47 @@ var HTMx = HTMx || (function () {
if (this.status !== 204) {
// Success!
var resp = this.response;
if (!triggerEvent(elt, 'beforeSwap.hx', {xhr: xhr, target: target})) return;
if (!triggerEvent(elt, 'beforeSwap.kutty', {xhr: xhr, target: target})) return;
// Save current page
if (shouldSaveHistory) {
saveHistory();
}
target.classList.add("hx-swapping");
target.classList.add("kutty-swapping");
var doSwap = function () {
try {
var settleTasks = swapResponse(target, elt, resp);
target.classList.remove("hx-swapping");
target.classList.add("hx-settling");
triggerEvent(elt, 'afterSwap.hx', {xhr: xhr, target: target});
target.classList.remove("kutty-swapping");
target.classList.add("kutty-settling");
triggerEvent(elt, 'afterSwap.kutty', {xhr: xhr, target: target});
var doSettle = function(){
forEach(settleTasks, function (settleTask) {
settleTask.call();
});
target.classList.remove("hx-settling");
target.classList.remove("kutty-settling");
// push URL and save new page
if (shouldSaveHistory) {
pushUrlIntoHistory(pushedUrl || requestURL );
saveHistory();
}
triggerEvent(elt, 'afterSettle.hx', {xhr: xhr, target: target});
triggerEvent(elt, 'afterSettle.kutty', {xhr: xhr, target: target});
}
var settleDelayStr = getAttributeValue(elt, "hx-settle-delay") || "100ms";
var settleDelayStr = getAttributeValue(elt, "kt-settle-delay") || "100ms";
if (settleDelayStr) {
setTimeout(doSettle, parseInterval(settleDelayStr))
} else {
doSettle();
}
} catch (e) {
triggerEvent(elt, 'swapError.hx', {xhr: xhr, response: xhr.response, status: xhr.status, target: target});
triggerEvent(elt, 'swapError.kutty', {xhr: xhr, response: xhr.response, status: xhr.status, target: target});
throw e;
}
};
var swapDelayStr = getAttributeValue(elt, "hx-swap-delay");
var swapDelayStr = getAttributeValue(elt, "kt-swap-delay");
if (swapDelayStr) {
setTimeout(doSwap, parseInterval(swapDelayStr))
} else {
@@ -908,22 +912,22 @@ var HTMx = HTMx || (function () {
}
}
} else {
triggerEvent(elt, 'responseError.hx', {xhr: xhr, response: xhr.response, status: xhr.status, target: target});
triggerEvent(elt, 'responseError.kutty', {xhr: xhr, response: xhr.response, status: xhr.status, target: target});
}
} catch (e) {
triggerEvent(elt, 'onLoadError.hx', {xhr: xhr, response: xhr.response, status: xhr.status, target: target});
triggerEvent(elt, 'onLoadError.kutty', {xhr: xhr, response: xhr.response, status: xhr.status, target: target});
throw e;
} finally {
removeRequestIndicatorClasses(elt);
endRequestLock();
triggerEvent(elt, 'afterOnLoad.hx', {xhr: xhr, response: xhr.response, status: xhr.status, target: target});
triggerEvent(elt, 'afterOnLoad.kutty', {xhr: xhr, response: xhr.response, status: xhr.status, target: target});
}
}
xhr.onerror = function () {
removeRequestIndicatorClasses(elt);triggerEvent(elt, 'loadError.hx', {xhr:xhr});
removeRequestIndicatorClasses(elt);triggerEvent(elt, 'loadError.kutty', {xhr:xhr});
endRequestLock();
}
if(!triggerEvent(elt, 'beforeRequest.hx', {xhr:xhr, values: inputValues, target:target})) return endRequestLock();
if(!triggerEvent(elt, 'beforeRequest.kutty', {xhr:xhr, values: inputValues, target:target})) return endRequestLock();
addRequestIndicatorClasses(elt);
xhr.send(verb === 'get' ? null : urlEncode(inputValues));
}
@@ -955,8 +959,8 @@ var HTMx = HTMx || (function () {
// Public API
return {
processElement: processNode,
on: addHTMxEventListener,
version: "0.0.2",
on: addKuttyEventListener,
version: "0.0.1",
_:internalEval
}
}