mirror of
https://github.com/bigskysoftware/htmx.git
synced 2025-10-02 07:21:05 +00:00
logging infrastructure
This commit is contained in:
parent
a354200743
commit
b09f8970fd
5
TODO.md
5
TODO.md
@ -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
|
||||||
|
161
src/htmx.js
161
src/htmx.js
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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, {}, "");
|
||||||
});
|
});
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user