mirror of
https://github.com/bigskysoftware/htmx.git
synced 2025-09-30 22:41:23 +00:00
logging infrastructure
This commit is contained in:
parent
a354200743
commit
b09f8970fd
5
TODO.md
5
TODO.md
@ -12,7 +12,6 @@
|
||||
|
||||
## TODOS
|
||||
|
||||
* 'revealed' event
|
||||
* history support
|
||||
* Implement LRU
|
||||
* Issue GET to restore content if there isn't a copy locally
|
||||
@ -37,10 +36,6 @@
|
||||
|
||||
* sse support
|
||||
|
||||
## Maybes
|
||||
|
||||
* hx-error-url
|
||||
|
||||
## Unsupported Intercooler Features
|
||||
|
||||
* local actions
|
||||
|
161
src/htmx.js
161
src/htmx.js
@ -71,8 +71,16 @@ var HTMx = HTMx || (function () {
|
||||
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) {
|
||||
return Object.prototype.toString.call(o) === "[object Object]";
|
||||
return isType(o, "Object");
|
||||
}
|
||||
|
||||
function getInternalData(elt) {
|
||||
@ -91,12 +99,20 @@ var HTMx = HTMx || (function () {
|
||||
});
|
||||
return arr;
|
||||
}
|
||||
|
||||
function forEach(arr, func) {
|
||||
for (var i = 0; i < arr.length; 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
|
||||
//====================================================================
|
||||
@ -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) {
|
||||
if (trigger) {
|
||||
if (trigger.indexOf("{") === 0) {
|
||||
@ -271,7 +275,6 @@ var HTMx = HTMx || (function () {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getTrigger(elt) {
|
||||
var explicitTrigger = getClosestAttributeValue(elt, 'hx-trigger');
|
||||
if (explicitTrigger) {
|
||||
@ -388,6 +391,26 @@ var HTMx = HTMx || (function () {
|
||||
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) {
|
||||
var nodeData = getInternalData(elt);
|
||||
if (!nodeData.processed) {
|
||||
@ -397,8 +420,13 @@ var HTMx = HTMx || (function () {
|
||||
forEach(VERBS, function(verb){
|
||||
var path = getAttributeValue(elt, 'hx-' + verb);
|
||||
if (path) {
|
||||
nodeData.path = path;
|
||||
nodeData.verb = verb;
|
||||
explicitAction = true;
|
||||
if (trigger === 'load') {
|
||||
if (trigger === 'revealed') {
|
||||
initScrollHandler();
|
||||
maybeReveal(elt);
|
||||
} else if (trigger === 'load') {
|
||||
if (!nodeData.loaded) {
|
||||
nodeData.loaded = true;
|
||||
issueAjaxRequest(elt, verb, path);
|
||||
@ -424,6 +452,63 @@ var HTMx = HTMx || (function () {
|
||||
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
|
||||
//====================================================================
|
||||
@ -585,7 +670,7 @@ var HTMx = HTMx || (function () {
|
||||
|
||||
// include the closest form
|
||||
processInputValue(processed, values, closest(elt, 'form'));
|
||||
return Object.keys(values).length === 0 ? null : values;
|
||||
return values;
|
||||
}
|
||||
|
||||
function appendParam(returnStr, name, realValue) {
|
||||
@ -650,7 +735,8 @@ var HTMx = HTMx || (function () {
|
||||
|
||||
// request type
|
||||
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 {
|
||||
xhr.open('POST', path, 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 () {
|
||||
try {
|
||||
if(!triggerEvent(elt, 'beforeOnLoad.hx', {xhr:xhr, target:target})) return;
|
||||
if (!triggerEvent(elt, 'beforeOnLoad.hx', {xhr: xhr, target: target})) return;
|
||||
snapshotForCurrentHistoryEntry(elt, path);
|
||||
var trigger = this.getResponseHeader("X-HX-Trigger");
|
||||
handleTrigger(elt, trigger);
|
||||
@ -694,37 +780,43 @@ 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.hx', {xhr: xhr, target: target})) return;
|
||||
target.classList.add("hx-swapping");
|
||||
var doSwap = function(){
|
||||
swapResponse(target, elt, resp, function(){
|
||||
target.classList.remove("hx-swapping");
|
||||
updateCurrentHistoryContent();
|
||||
triggerEvent(elt, 'afterSwap.hx', {xhr:xhr, target:target});
|
||||
});
|
||||
}
|
||||
var doSwap = function () {
|
||||
try {
|
||||
swapResponse(target, elt, resp, function () {
|
||||
target.classList.remove("hx-swapping");
|
||||
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");
|
||||
if (swapDelayStr) {
|
||||
setTimeout(doSwap(), parseInterval(swapDelayStr))
|
||||
setTimeout(doSwap, parseInterval(swapDelayStr))
|
||||
} else {
|
||||
doSwap();
|
||||
}
|
||||
}
|
||||
} 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 {
|
||||
removeRequestIndicatorClasses(elt);
|
||||
triggerEvent(elt, 'afterOnLoad.hx', {xhr:xhr, response: xhr.response, status: xhr.status, target:target});
|
||||
endRequestLock();
|
||||
triggerEvent(elt, 'afterOnLoad.hx', {xhr: xhr, response: xhr.response, status: xhr.status, target: target});
|
||||
}
|
||||
};
|
||||
}
|
||||
xhr.onerror = function () {
|
||||
removeRequestIndicatorClasses(elt);
|
||||
triggerEvent(elt, 'onError.hx', {xhr:xhr});
|
||||
removeRequestIndicatorClasses(elt);triggerEvent(elt, 'loadError.hx', {xhr:xhr});
|
||||
endRequestLock();
|
||||
};
|
||||
|
||||
}
|
||||
if(!triggerEvent(elt, 'beforeRequest.hx', {xhr:xhr, values: inputValues, target:target})) return endRequestLock();
|
||||
addRequestIndicatorClasses(elt);
|
||||
xhr.send(verb === 'get' ? null : urlEncode(inputValues));
|
||||
@ -757,6 +849,7 @@ var HTMx = HTMx || (function () {
|
||||
// Public API
|
||||
return {
|
||||
processElement: processNode,
|
||||
on: addHTMxEventListener,
|
||||
version: "0.0.1",
|
||||
_:internalEval
|
||||
}
|
||||
|
@ -40,7 +40,6 @@ describe("HTMx AJAX Headers Tests", function() {
|
||||
|
||||
it("should include the X-HX-Target-Id header", function(){
|
||||
this.server.respondWith("GET", "/test", function(xhr){
|
||||
console.log(xhr.requestHeaders);
|
||||
xhr.requestHeaders['X-HX-Target-Id'].should.equal('d1');
|
||||
xhr.respond(200, {}, "");
|
||||
});
|
||||
|
@ -1,4 +1,11 @@
|
||||
/* Test Utilities */
|
||||
|
||||
HTMx.logger = function(elt, event, data) {
|
||||
if(console) {
|
||||
console.log(event, elt, data);
|
||||
}
|
||||
}
|
||||
|
||||
function byId(id) {
|
||||
return document.getElementById(id);
|
||||
}
|
||||
|
@ -8,12 +8,6 @@ describe("HTMx Value Handling", function() {
|
||||
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 () {
|
||||
var input = make('<input name="foo" value="bar"/>');
|
||||
var vals = HTMx._('getInputValues')(input);
|
||||
|
Loading…
x
Reference in New Issue
Block a user