update website

This commit is contained in:
carson 2020-05-09 13:49:44 -07:00
parent d08110ed22
commit 3b84f86319
3 changed files with 194 additions and 94 deletions

View File

@ -23,7 +23,7 @@
<div class="c"> <div class="c">
<div class="row"> <div class="row">
<div class="2 col" kt-get="/" kt-target="body" kt-push-url="true"> <div class="2 col" kt-get="/" kt-target="body" kt-push-url="true">
<span class="logo light" kt-add-class="settle">&lt;<a>/</a>&gt; k<a>u</a>tty</span> <span class="logo light">&lt;<a>/</a>&gt; k<a>u</a>tty</span>
</div> </div>
{% if page.url.indexOf("/patterns/") == 0 %} {% if page.url.indexOf("/patterns/") == 0 %}
<div class="10 col nav"> <!-- don't boost on demo pages, sinon hijacks everything :/ --> <div class="10 col nav"> <!-- don't boost on demo pages, sinon hijacks everything :/ -->

View File

@ -3,7 +3,7 @@ layout: layout.njk
title: </> kutty - high power tools for html title: </> kutty - high power tools for html
--- ---
<div class="dark-hero full-width" kt-add-class="appear"> <div class="dark-hero full-width" kt-classes="add appear">
<span class="logo dark">&lt;<a>/</a>&gt; <span class="no-mobile">k<a>u</a>tty</span></span> <span class="logo dark">&lt;<a>/</a>&gt; <span class="no-mobile">k<a>u</a>tty</span></span>
<sub class="no-mobile"><i>high powered tools for HTML</i></sub> <sub class="no-mobile"><i>high powered tools for HTML</i></sub>
</div> </div>

View File

@ -118,6 +118,10 @@ var kutty = kutty || (function () {
return arr1.concat(arr2); return arr1.concat(arr2);
} }
function splitOnWhitespace(trigger) {
return trigger.split(/\s+/);
}
//==================================================================== //====================================================================
// Node processing // Node processing
//==================================================================== //====================================================================
@ -208,19 +212,19 @@ var kutty = kutty || (function () {
} }
} }
function swapPrepend(target, fragment) { function swapAfterBegin(target, fragment) {
return insertNodesBefore(target, target.firstChild, fragment); return insertNodesBefore(target, target.firstChild, fragment);
} }
function swapPrependBefore(target, fragment) { function swapBeforeBegin(target, fragment) {
return insertNodesBefore(parentElt(target), target, fragment); return insertNodesBefore(parentElt(target), target, fragment);
} }
function swapAppend(target, fragment) { function swapBeforeEnd(target, fragment) {
return insertNodesBefore(target, null, fragment); return insertNodesBefore(target, null, fragment);
} }
function swapAppendAfter(target, fragment) { function swapAfterEnd(target, fragment) {
return insertNodesBefore(parentElt(target), target.nextSibling, fragment); return insertNodesBefore(parentElt(target), target.nextSibling, fragment);
} }
@ -248,20 +252,19 @@ var kutty = kutty || (function () {
return fragment; return fragment;
} }
function swapResponse(target, elt, responseText) { function swapResponse(swapStyle, target, elt, responseText) {
var fragment = makeFragment(responseText); var fragment = makeFragment(responseText);
if (fragment) { if (fragment) {
var settleTasks = handleOutOfBandSwaps(fragment); var settleTasks = handleOutOfBandSwaps(fragment);
fragment = maybeSelectFromResponse(elt, fragment); fragment = maybeSelectFromResponse(elt, fragment);
var swapStyle = getClosestAttributeValue(elt, "kt-swap");
switch(swapStyle) { switch(swapStyle) {
case "outerHTML": return concat(settleTasks, swapOuterHTML(target, fragment)); case "outerHTML": return concat(settleTasks, swapOuterHTML(target, fragment));
case "prepend": return concat(settleTasks, swapPrepend(target, fragment)); case "afterbegin": return concat(settleTasks, swapAfterBegin(target, fragment));
case "prependBefore": return concat(settleTasks, swapPrependBefore(target, fragment)); case "beforebegin": return concat(settleTasks, swapBeforeBegin(target, fragment));
case "append": return concat(settleTasks, swapAppend(target, fragment)); case "beforeend": return concat(settleTasks, swapBeforeEnd(target, fragment));
case "appendAfter": return concat(settleTasks, swapAppendAfter(target, fragment)); case "afterend": return concat(settleTasks, swapAfterEnd(target, fragment));
default: return concat(settleTasks, swapInnerHTML(target, fragment)); default: return concat(settleTasks, swapInnerHTML(target, fragment));
} }
} }
@ -286,58 +289,104 @@ var kutty = kutty || (function () {
} }
} }
function getTrigger(elt) { function getTriggerSpec(elt) {
var triggerSpec = {
"trigger" : "click"
}
var explicitTrigger = getClosestAttributeValue(elt, 'kt-trigger'); var explicitTrigger = getClosestAttributeValue(elt, 'kt-trigger');
if (explicitTrigger) { if (explicitTrigger) {
return explicitTrigger; var tokens = splitOnWhitespace(explicitTrigger);
} else { if (tokens.length > 0) {
if (matches(elt, 'button')) { var trigger = tokens[0];
return 'click'; if (trigger === "every") {
} else if (matches(elt, 'form')) { triggerSpec.pollInterval = parseInterval(tokens[1]);
return 'submit'; } else if (trigger.indexOf("sse:") === 0) {
} else if (matches(elt, 'input, textarea, select')) { triggerSpec.sseEvent = trigger.substr(4);
return 'change'; } else {
} else { triggerSpec['trigger'] = trigger;
return 'click'; for (var i = 1; i < tokens.length; i++) {
var token = tokens[i].trim();
if (token === "changed") {
triggerSpec.changed = true;
}
if (token === "once") {
triggerSpec.once = true;
}
if (token.indexOf("delay:") === 0) {
triggerSpec.delay = parseInterval(token.substr(6));
}
}
}
} }
} else {
if (matches(elt, 'form')) {
triggerSpec['trigger'] = 'submit';
} else if (matches(elt, 'input, textarea, select')) {
triggerSpec['trigger'] = 'change';
}
}
return triggerSpec;
}
function parseClassOperation(trimmedValue) {
var split = splitOnWhitespace(trimmedValue);
if (split.length > 1) {
var operation = split[0];
var classDef = split[1].trim();
var cssClass;
var delay;
if (classDef.indexOf(":") > 0) {
var splitCssClass = classDef.split(':');
cssClass = splitCssClass[0];
delay = parseInterval(splitCssClass[1]);
} else {
cssClass = classDef;
delay = 100;
}
return {
operation:operation,
cssClass:cssClass,
delay:delay
}
} else {
return null;
} }
} }
function processClassList(elt, classList, operation) { function processClassList(elt, classList, operation) {
var values = classList.split(","); forEach(classList.split("&"), function (run) {
forEach(values, function(value){ var currentRunTime = 0;
var cssClass = ""; forEach(run.split(","), function(value){
var delay = 50; var trimmedValue = value.trim();
var trimmedValue = value.trim(); var classOperation = parseClassOperation(trimmedValue);
if (trimmedValue.indexOf(":") > 0) { if (classOperation) {
var split = trimmedValue.split(':'); if (classOperation.operation === "toggle") {
cssClass = split[0]; setTimeout(function () {
delay = parseInterval(split[1]); setInterval(function () {
} else { elt.classList[classOperation.operation].call(elt.classList, classOperation.cssClass);
cssClass = trimmedValue; }, classOperation.delay);
} }, currentRunTime);
setTimeout(function () { currentRunTime = currentRunTime + classOperation.delay;
elt.classList[operation].call(elt.classList, cssClass); } else {
}, delay); currentRunTime = currentRunTime + classOperation.delay;
setTimeout(function () {
elt.classList[classOperation.operation].call(elt.classList, classOperation.cssClass);
}, currentRunTime);
}
}
});
}); });
} }
function processPolling(elt, verb, path) { function processPolling(elt, verb, path, interval) {
var trigger = getTrigger(elt);
var nodeData = getInternalData(elt); var nodeData = getInternalData(elt);
if (trigger.trim().indexOf("every ") === 0) { nodeData.timeout = setTimeout(function () {
var args = trigger.split(/\s+/); if (bodyContains(elt)) {
var intervalStr = args[1]; issueAjaxRequest(elt, verb, path);
if (intervalStr) { processPolling(elt, verb, getAttributeValue(elt, "kt-" + verb), interval);
var interval = parseInterval(intervalStr);
nodeData.timeout = setTimeout(function () {
if (bodyContains(elt)) {
issueAjaxRequest(elt, verb, path);
processPolling(elt, verb, getAttributeValue(elt, "kt-" + verb));
}
}, interval);
} }
} }, interval);
} }
function isLocalLink(elt) { function isLocalLink(elt) {
@ -346,7 +395,7 @@ var kutty = kutty || (function () {
!getRawAttribute(elt,'href').startsWith("#") !getRawAttribute(elt,'href').startsWith("#")
} }
function boostElement(elt, nodeData, trigger) { function boostElement(elt, nodeData, triggerSpec) {
if ((elt.tagName === "A" && isLocalLink(elt)) || elt.tagName === "FORM") { if ((elt.tagName === "A" && isLocalLink(elt)) || elt.tagName === "FORM") {
nodeData.boosted = true; nodeData.boosted = true;
var verb, path; var verb, path;
@ -358,31 +407,31 @@ var kutty = kutty || (function () {
verb = rawAttribute ? rawAttribute.toLowerCase() : "get"; verb = rawAttribute ? rawAttribute.toLowerCase() : "get";
path = getRawAttribute(elt, 'action'); path = getRawAttribute(elt, 'action');
} }
addEventListener(elt, verb, path, nodeData, trigger, true); addEventListener(elt, verb, path, nodeData, triggerSpec, true);
} }
} }
function shouldCancel(elt) { function shouldCancel(elt) {
return elt.tagName === "FORM" || return elt.tagName === "FORM" ||
(matches(elt, 'input[type="submit"], button') && closest(elt, 'form') !== null) || (matches(elt, 'input[type="submit"], button') && closest(elt, 'form') !== null) ||
(elt.tagName === "A" && elt.href && elt.href.indexOf('#') != 0); (elt.tagName === "A" && elt.href && elt.href.indexOf('#') !== 0);
} }
function addEventListener(elt, verb, path, nodeData, trigger, explicitCancel) { function addEventListener(elt, verb, path, nodeData, triggerSpec, explicitCancel) {
var eventListener = function (evt) { var eventListener = function (evt) {
if(explicitCancel || shouldCancel(elt)) evt.preventDefault(); if(explicitCancel || shouldCancel(elt)) evt.preventDefault();
var eventData = getInternalData(evt); var eventData = getInternalData(evt);
var elementData = getInternalData(elt); var elementData = getInternalData(elt);
if (!eventData.handled) { if (!eventData.handled) {
eventData.handled = true; eventData.handled = true;
if (getAttributeValue(elt, "kt-trigger-once") === "true") { if (triggerSpec.once) {
if (elementData.triggeredOnce) { if (elementData.triggeredOnce) {
return; return;
} else { } else {
elementData.triggeredOnce = true; elementData.triggeredOnce = true;
} }
} }
if (getAttributeValue(elt, "kt-trigger-changed-only") === "true") { if (triggerSpec.changed) {
if (elementData.lastValue === elt.value) { if (elementData.lastValue === elt.value) {
return; return;
} else { } else {
@ -392,20 +441,19 @@ var kutty = kutty || (function () {
if (elementData.delayed) { if (elementData.delayed) {
clearTimeout(elementData.delayed); clearTimeout(elementData.delayed);
} }
var eventDelay = getAttributeValue(elt, "kt-trigger-delay");
var issueRequest = function(){ var issueRequest = function(){
issueAjaxRequest(elt, verb, path, evt.target); issueAjaxRequest(elt, verb, path, evt.target);
} }
if (eventDelay) { if (triggerSpec.delay) {
elementData.delayed = setTimeout(issueRequest, parseInterval(eventDelay)); elementData.delayed = setTimeout(issueRequest, parseInterval(triggerSpec.delay));
} else { } else {
issueRequest(); issueRequest();
} }
} }
}; };
nodeData.trigger = trigger; nodeData.trigger = triggerSpec.trigger;
nodeData.eventListener = eventListener; nodeData.eventListener = eventListener;
elt.addEventListener(trigger, eventListener); elt.addEventListener(triggerSpec.trigger, eventListener);
} }
function initScrollHandler() { function initScrollHandler() {
@ -476,7 +524,7 @@ var kutty = kutty || (function () {
} }
} }
function processVerbs(elt, nodeData, trigger) { function processVerbs(elt, nodeData, triggerSpec) {
var explicitAction = false; var explicitAction = false;
forEach(VERBS, function (verb) { forEach(VERBS, function (verb) {
var path = getAttributeValue(elt, 'kt-' + verb); var path = getAttributeValue(elt, 'kt-' + verb);
@ -484,18 +532,18 @@ var kutty = kutty || (function () {
explicitAction = true; explicitAction = true;
nodeData.path = path; nodeData.path = path;
nodeData.verb = verb; nodeData.verb = verb;
if (trigger.indexOf("sse:") === 0) { if (triggerSpec.sseEvent) {
processSSETrigger(trigger.substr(4), elt, verb, path); processSSETrigger(triggerSpec.sseEvent, elt, verb, path);
} else if (trigger === 'revealed') { } else if (triggerSpec.trigger === "revealed") {
initScrollHandler(); initScrollHandler();
maybeReveal(elt); maybeReveal(elt);
} else if (trigger === 'load') { } else if (triggerSpec.trigger === "load") {
loadImmediately(nodeData, elt, verb, path); loadImmediately(nodeData, elt, verb, path);
} else if (trigger.trim().indexOf('every ') === 0) { } else if (triggerSpec.pollInterval) {
nodeData.polling = true; nodeData.polling = true;
processPolling(elt, verb, path); processPolling(elt, verb, path, triggerSpec.pollInterval);
} else { } else {
addEventListener(elt, verb, path, nodeData, trigger); addEventListener(elt, verb, path, nodeData, triggerSpec);
} }
} }
}); });
@ -507,23 +555,19 @@ var kutty = kutty || (function () {
if (!nodeData.processed) { if (!nodeData.processed) {
nodeData.processed = true; nodeData.processed = true;
var trigger = getTrigger(elt); var triggerSpec = getTriggerSpec(elt);
var explicitAction = processVerbs(elt, nodeData, trigger); var explicitAction = processVerbs(elt, nodeData, triggerSpec);
if (!explicitAction && getClosestAttributeValue(elt, "kt-boost") === "true") { if (!explicitAction && getClosestAttributeValue(elt, "kt-boost") === "true") {
boostElement(elt, nodeData, trigger); boostElement(elt, nodeData, triggerSpec);
} }
var sseSrc = getAttributeValue(elt, 'kt-sse-source'); var sseSrc = getAttributeValue(elt, 'kt-sse-source');
if (sseSrc) { if (sseSrc) {
initSSESource(elt, sseSrc); initSSESource(elt, sseSrc);
} }
var addClass = getAttributeValue(elt, 'kt-add-class'); var addClass = getAttributeValue(elt, 'kt-classes');
if (addClass) { if (addClass) {
processClassList(elt, addClass, "add"); processClassList(elt, addClass);
}
var removeClass = getAttributeValue(elt, 'kt-remove-class');
if (removeClass) {
processClassList(elt, removeClass, "remove");
} }
} }
if (elt.children) { // IE if (elt.children) { // IE
@ -598,7 +642,7 @@ var kutty = kutty || (function () {
// History Support // History Support
//==================================================================== //====================================================================
function getHistoryElement() { function getHistoryElement() {
var historyElt = getDocument().querySelector('.kt-history-element'); var historyElt = getDocument().querySelector('.kt-history-elt');
return historyElt || getDocument().body; return historyElt || getDocument().body;
} }
@ -653,7 +697,7 @@ var kutty = kutty || (function () {
triggerEvent(getDocument().body, "historyCacheMissLoad.kutty", {path: pathAndSearch}); triggerEvent(getDocument().body, "historyCacheMissLoad.kutty", {path: pathAndSearch});
if (this.status >= 200 && this.status < 400) { if (this.status >= 200 && this.status < 400) {
var fragment = makeFragment(this.response); var fragment = makeFragment(this.response);
fragment = fragment.querySelector('.kt-history-element') || fragment; fragment = fragment.querySelector('.kt-history-elt') || fragment;
settleImmediately(swapInnerHTML(getHistoryElement(), fragment)); settleImmediately(swapInnerHTML(getHistoryElement(), fragment));
} }
}; };
@ -812,6 +856,61 @@ var kutty = kutty || (function () {
} }
} }
function filterValues(inputValues, elt, verb) {
var paramsValue = getClosestAttributeValue(elt, "kt-params");
if (paramsValue) {
if (paramsValue === "none") {
return {};
} else if (paramsValue === "*") {
return inputValues;
} else if(paramsValue.indexOf("not ") === 0) {
forEach(paramsValue.substr(4).split(","), function (value) {
value = value.trim();
delete inputValues[value];
});
return inputValues;
} else {
var newValues = {}
forEach(paramsValue.split(","), function (value) {
newValues[value] = inputValues[value];
});
return newValues;
}
} else {
// By default GET does not include parameters
if (verb === 'get') {
return {};
} else {
return inputValues;
}
}
}
function getSwapSpecification(elt) {
var swapInfo = getClosestAttributeValue(elt, "kt-swap");
var swapSpec = {
"swapStyle" : "innerHTML",
"swapDelay" : 0,
"settleDelay" : 100
}
if (swapInfo) {
var split = splitOnWhitespace(swapInfo);
if (split.length > 0) {
swapSpec["swapStyle"] = split[0];
for (var i = 1; i < swapSpec.length; i++) {
var modifier = swapSpec[i];
if (modifier.indexOf("swap:") === 0) {
swapSpec["swapDelay"] = parseInterval(modifier.substr(5));
}
if (modifier.indexOf("settle:") === 0) {
swapSpec["settleDelay"] = parseInterval(modifier.substr(7));
}
}
}
}
return swapSpec;
}
function issueAjaxRequest(elt, verb, path, eventTarget) { function issueAjaxRequest(elt, verb, path, eventTarget) {
var eltData = getInternalData(elt); var eltData = getInternalData(elt);
if (eltData.requestInFlight) { if (eltData.requestInFlight) {
@ -836,16 +935,17 @@ var kutty = kutty || (function () {
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
var inputValues = getInputValues(elt); var inputValues = getInputValues(elt, verb);
inputValues = filterValues(inputValues, elt, verb);
if(!triggerEvent(elt, 'values.kutty', {values: inputValues, target:target})) return endRequestLock(); if(!triggerEvent(elt, 'values.kutty', {values: inputValues, target:target})) return endRequestLock();
// request type // request type
var requestURL; var requestURL;
if (verb === 'get') { if (verb === 'get') {
var includeQueryParams = getClosestAttributeValue("kt-get-params") === "true"; var noValues = Object.keys(inputValues).length === 0;
// TODO allow get parameter filtering xhr.open('GET', path + (noValues ? "" : "?" + urlEncode(inputValues)), true);
requestURL = path + (includeQueryParams ? "?" + urlEncode(inputValues) : "");
xhr.open('GET', requestURL, true);
} else { } else {
requestURL = path; requestURL = path;
xhr.open('POST', requestURL, true); xhr.open('POST', requestURL, true);
@ -882,10 +982,12 @@ var kutty = kutty || (function () {
saveHistory(); saveHistory();
} }
var swapSpec = getSwapSpecification(elt);
target.classList.add("kutty-swapping"); target.classList.add("kutty-swapping");
var doSwap = function () { var doSwap = function () {
try { try {
var settleTasks = swapResponse(target, elt, resp); var settleTasks = swapResponse(swapSpec.swapStyle, target, elt, resp);
target.classList.remove("kutty-swapping"); target.classList.remove("kutty-swapping");
target.classList.add("kutty-settling"); target.classList.add("kutty-settling");
triggerEvent(elt, 'afterSwap.kutty', {xhr: xhr, target: target}); triggerEvent(elt, 'afterSwap.kutty', {xhr: xhr, target: target});
@ -903,9 +1005,8 @@ var kutty = kutty || (function () {
triggerEvent(elt, 'afterSettle.kutty', {xhr: xhr, target: target}); triggerEvent(elt, 'afterSettle.kutty', {xhr: xhr, target: target});
} }
var settleDelayStr = getAttributeValue(elt, "kt-settle-delay") || "100ms"; if (swapSpec.settleDelay > 0) {
if (settleDelayStr) { setTimeout(doSettle, swapSpec.settleDelay)
setTimeout(doSettle, parseInterval(settleDelayStr))
} else { } else {
doSettle(); doSettle();
} }
@ -915,9 +1016,8 @@ var kutty = kutty || (function () {
} }
}; };
var swapDelayStr = getAttributeValue(elt, "kt-swap-delay"); if (swapSpec.swapDelay > 0) {
if (swapDelayStr) { setTimeout(doSwap, parseInterval(swapSpec.swapDelay))
setTimeout(doSwap, parseInterval(swapDelayStr))
} else { } else {
doSwap(); doSwap();
} }