logging infrastructure

This commit is contained in:
carson 2020-05-02 09:22:09 -07:00
parent a354200743
commit b09f8970fd
5 changed files with 134 additions and 46 deletions

View File

@ -12,7 +12,6 @@
## TODOS ## TODOS
* 'revealed' event
* history support * history support
* Implement LRU * Implement LRU
* Issue GET to restore content if there isn't a copy locally * Issue GET to restore content if there isn't a copy locally
@ -37,10 +36,6 @@
* sse support * sse support
## Maybes
* hx-error-url
## Unsupported Intercooler Features ## Unsupported Intercooler Features
* local actions * local actions

View File

@ -71,8 +71,16 @@ var HTMx = HTMx || (function () {
return range.createContextualFragment(resp); return range.createContextualFragment(resp);
} }
function isType(o, type) {
return Object.prototype.toString.call(o) === "[object " + type + "]";
}
function isFunction(o) {
return isType(o, "Function");
}
function isRawObject(o) { function isRawObject(o) {
return Object.prototype.toString.call(o) === "[object Object]"; return isType(o, "Object");
} }
function getInternalData(elt) { function getInternalData(elt) {
@ -91,12 +99,20 @@ var HTMx = HTMx || (function () {
}); });
return arr; return arr;
} }
function forEach(arr, func) { function forEach(arr, func) {
for (var i = 0; i < arr.length; i++) { for (var i = 0; i < arr.length; i++) {
func(arr[i]); func(arr[i]);
} }
} }
function isScrolledIntoView(el) {
var rect = el.getBoundingClientRect();
var elemTop = rect.top;
var elemBottom = rect.bottom;
return elemTop < window.innerHeight && elemBottom >= 0;
}
//==================================================================== //====================================================================
// Node processing // Node processing
//==================================================================== //====================================================================
@ -240,18 +256,6 @@ var HTMx = HTMx || (function () {
} }
} }
function triggerEvent(elt, eventName, details) {
details["elt"] = elt;
var event;
if (window.CustomEvent && typeof window.CustomEvent === 'function') {
event = new CustomEvent(eventName, {detail: details});
} else {
event = getDocument().createEvent('CustomEvent');
event.initCustomEvent(eventName, true, true, details);
}
return elt.dispatchEvent(event);
}
function handleTrigger(elt, trigger) { function handleTrigger(elt, trigger) {
if (trigger) { if (trigger) {
if (trigger.indexOf("{") === 0) { if (trigger.indexOf("{") === 0) {
@ -271,7 +275,6 @@ var HTMx = HTMx || (function () {
} }
} }
function getTrigger(elt) { function getTrigger(elt) {
var explicitTrigger = getClosestAttributeValue(elt, 'hx-trigger'); var explicitTrigger = getClosestAttributeValue(elt, 'hx-trigger');
if (explicitTrigger) { if (explicitTrigger) {
@ -388,6 +391,26 @@ var HTMx = HTMx || (function () {
elt.addEventListener(trigger, eventListener); elt.addEventListener(trigger, eventListener);
} }
function initScrollHandler() {
if (!window['hxScrollHandler']) {
var scrollHandler = function() {
forEach(getDocument().querySelectorAll("[hx-trigger='reveal']"), function (elt) {
maybeReveal(elt);
});
};
window['hxScrollHandler'] = scrollHandler;
window.addEventListener("scroll", scrollHandler)
}
}
function maybeReveal(elt) {
var nodeData = getInternalData(elt);
if (!nodeData.revealed && isScrolledIntoView(elt)) {
nodeData.revealed = true;
issueAjaxRequest(elt, nodeData.verb, nodeData.path);
}
}
function processNode(elt) { function processNode(elt) {
var nodeData = getInternalData(elt); var nodeData = getInternalData(elt);
if (!nodeData.processed) { if (!nodeData.processed) {
@ -397,8 +420,13 @@ var HTMx = HTMx || (function () {
forEach(VERBS, function(verb){ forEach(VERBS, function(verb){
var path = getAttributeValue(elt, 'hx-' + verb); var path = getAttributeValue(elt, 'hx-' + verb);
if (path) { if (path) {
nodeData.path = path;
nodeData.verb = verb;
explicitAction = true; explicitAction = true;
if (trigger === 'load') { if (trigger === 'revealed') {
initScrollHandler();
maybeReveal(elt);
} else if (trigger === 'load') {
if (!nodeData.loaded) { if (!nodeData.loaded) {
nodeData.loaded = true; nodeData.loaded = true;
issueAjaxRequest(elt, verb, path); issueAjaxRequest(elt, verb, path);
@ -424,6 +452,63 @@ var HTMx = HTMx || (function () {
forEach(elt.children, function(child) { processNode(child) }); forEach(elt.children, function(child) { processNode(child) });
} }
//====================================================================
// Event/Log Support
//====================================================================
function sendError(elt, eventName, details) {
var errorURL = getClosestAttributeValue(elt, "hx-error-url");
if (errorURL) {
var xhr = new XMLHttpRequest();
xhr.open("POST", errorURL);
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xhr.send(JSON.stringify({ "elt": elt.id, "event": eventName, "details" : details }));
}
}
function makeEvent(eventName, details) {
var evt;
if (window.CustomEvent && typeof window.CustomEvent === 'function') {
evt = new CustomEvent(eventName, {detail: details});
} else {
evt = getDocument().createEvent('CustomEvent');
evt.initCustomEvent(eventName, true, true, details);
}
return evt;
}
function triggerEvent(elt, eventName, details) {
details["elt"] = elt;
var event = makeEvent(eventName, details);
if (HTMx.logger) {
HTMx.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}));
return eventResult && allResult;
}
function addHTMxEventListener(arg1, arg2, arg3) {
var target, event, listener;
if (isFunction(arg1)) {
target = getDocument().body;
event = "all.hx";
listener = arg1;
} else if (isFunction(arg2)) {
target = getDocument().body;
event = arg1;
listener = arg2;
} else {
target = arg1;
event = arg2;
listener = arg3;
}
return target.addEventListener(event, listener);
}
//==================================================================== //====================================================================
// History Support // History Support
//==================================================================== //====================================================================
@ -585,7 +670,7 @@ var HTMx = HTMx || (function () {
// include the closest form // include the closest form
processInputValue(processed, values, closest(elt, 'form')); processInputValue(processed, values, closest(elt, 'form'));
return Object.keys(values).length === 0 ? null : values; return values;
} }
function appendParam(returnStr, name, realValue) { function appendParam(returnStr, name, realValue) {
@ -650,7 +735,8 @@ var HTMx = HTMx || (function () {
// request type // request type
if (verb === 'get') { if (verb === 'get') {
xhr.open('GET', path + (inputValues ? "?" + urlEncode(inputValues) : ""), true); var noValues = Object.keys(inputValues).length === 0;
xhr.open('GET', path + (noValues ? "" : "?" + urlEncode(inputValues)), true);
} else { } else {
xhr.open('POST', path, true); xhr.open('POST', path, true);
setHeader(xhr,'Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8', true); setHeader(xhr,'Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8', true);
@ -684,7 +770,7 @@ var HTMx = HTMx || (function () {
xhr.onload = function () { xhr.onload = function () {
try { try {
if(!triggerEvent(elt, 'beforeOnLoad.hx', {xhr:xhr, target:target})) return; if (!triggerEvent(elt, 'beforeOnLoad.hx', {xhr: xhr, target: target})) return;
snapshotForCurrentHistoryEntry(elt, path); snapshotForCurrentHistoryEntry(elt, path);
var trigger = this.getResponseHeader("X-HX-Trigger"); var trigger = this.getResponseHeader("X-HX-Trigger");
handleTrigger(elt, trigger); handleTrigger(elt, trigger);
@ -694,37 +780,43 @@ var HTMx = HTMx || (function () {
if (this.status !== 204) { if (this.status !== 204) {
// Success! // Success!
var resp = this.response; var resp = this.response;
if(!triggerEvent(elt, 'beforeSwap.hx', {xhr:xhr, target:target})) return; if (!triggerEvent(elt, 'beforeSwap.hx', {xhr: xhr, target: target})) return;
target.classList.add("hx-swapping"); target.classList.add("hx-swapping");
var doSwap = function(){ var doSwap = function () {
swapResponse(target, elt, resp, function(){ try {
target.classList.remove("hx-swapping"); swapResponse(target, elt, resp, function () {
updateCurrentHistoryContent(); target.classList.remove("hx-swapping");
triggerEvent(elt, 'afterSwap.hx', {xhr:xhr, target:target}); updateCurrentHistoryContent();
}); triggerEvent(elt, 'afterSwap.hx', {xhr: xhr, target: target});
} });
} catch (e) {
triggerEvent(elt, 'swapError.hx', {xhr: xhr, response: xhr.response, status: xhr.status, target: target});
throw e;
}
};
var swapDelayStr = getAttributeValue(elt, "hx-swap-delay"); var swapDelayStr = getAttributeValue(elt, "hx-swap-delay");
if (swapDelayStr) { if (swapDelayStr) {
setTimeout(doSwap(), parseInterval(swapDelayStr)) setTimeout(doSwap, parseInterval(swapDelayStr))
} else { } else {
doSwap(); doSwap();
} }
} }
} else { } else {
triggerEvent(elt, 'errorResponse.hx', {xhr:xhr, response: xhr.response, status: xhr.status, target:target}); triggerEvent(elt, 'responseError.hx', {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});
throw e;
} finally { } finally {
removeRequestIndicatorClasses(elt); removeRequestIndicatorClasses(elt);
triggerEvent(elt, 'afterOnLoad.hx', {xhr:xhr, response: xhr.response, status: xhr.status, target:target});
endRequestLock(); endRequestLock();
triggerEvent(elt, 'afterOnLoad.hx', {xhr: xhr, response: xhr.response, status: xhr.status, target: target});
} }
}; }
xhr.onerror = function () { xhr.onerror = function () {
removeRequestIndicatorClasses(elt); removeRequestIndicatorClasses(elt);triggerEvent(elt, 'loadError.hx', {xhr:xhr});
triggerEvent(elt, 'onError.hx', {xhr:xhr});
endRequestLock(); endRequestLock();
}; }
if(!triggerEvent(elt, 'beforeRequest.hx', {xhr:xhr, values: inputValues, target:target})) return endRequestLock(); if(!triggerEvent(elt, 'beforeRequest.hx', {xhr:xhr, values: inputValues, target:target})) return endRequestLock();
addRequestIndicatorClasses(elt); addRequestIndicatorClasses(elt);
xhr.send(verb === 'get' ? null : urlEncode(inputValues)); xhr.send(verb === 'get' ? null : urlEncode(inputValues));
@ -757,6 +849,7 @@ var HTMx = HTMx || (function () {
// Public API // Public API
return { return {
processElement: processNode, processElement: processNode,
on: addHTMxEventListener,
version: "0.0.1", version: "0.0.1",
_:internalEval _:internalEval
} }

View File

@ -40,7 +40,6 @@ describe("HTMx AJAX Headers Tests", function() {
it("should include the X-HX-Target-Id header", function(){ it("should include the X-HX-Target-Id header", function(){
this.server.respondWith("GET", "/test", function(xhr){ this.server.respondWith("GET", "/test", function(xhr){
console.log(xhr.requestHeaders);
xhr.requestHeaders['X-HX-Target-Id'].should.equal('d1'); xhr.requestHeaders['X-HX-Target-Id'].should.equal('d1');
xhr.respond(200, {}, ""); xhr.respond(200, {}, "");
}); });

View File

@ -1,4 +1,11 @@
/* Test Utilities */ /* Test Utilities */
HTMx.logger = function(elt, event, data) {
if(console) {
console.log(event, elt, data);
}
}
function byId(id) { function byId(id) {
return document.getElementById(id); return document.getElementById(id);
} }

View File

@ -8,12 +8,6 @@ describe("HTMx Value Handling", function() {
clearWorkArea(); clearWorkArea();
}); });
it('No values evaluates to null', function () {
var div = make('<div></div>');
var vals = HTMx._('getInputValues')(div);
should.equal(vals, null);
})
it('Input includes value', function () { it('Input includes value', function () {
var input = make('<input name="foo" value="bar"/>'); var input = make('<input name="foo" value="bar"/>');
var vals = HTMx._('getInputValues')(input); var vals = HTMx._('getInputValues')(input);