2013-03-17 15:28:49 +00:00
|
|
|
/*\
|
|
|
|
title: $:/core/modules/utils/dom/http.js
|
|
|
|
type: application/javascript
|
|
|
|
module-type: utils
|
|
|
|
|
2022-12-12 08:54:40 +00:00
|
|
|
HTTP support
|
2013-03-17 15:28:49 +00:00
|
|
|
|
|
|
|
\*/
|
|
|
|
(function(){
|
|
|
|
|
|
|
|
/*jslint node: true, browser: true */
|
|
|
|
/*global $tw: false */
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
/*
|
2022-12-12 08:54:40 +00:00
|
|
|
Manage tm-http-request events. Options are:
|
|
|
|
wiki - the wiki object to use
|
|
|
|
*/
|
|
|
|
function HttpClient(options) {
|
|
|
|
options = options || {};
|
|
|
|
this.wiki = options.wiki || $tw.wiki;
|
|
|
|
}
|
|
|
|
|
|
|
|
HttpClient.prototype.handleHttpRequest = function(event) {
|
|
|
|
console.log("Making an HTTP request",event)
|
|
|
|
var self = this,
|
|
|
|
paramObject = event.paramObject || {},
|
|
|
|
url = paramObject.url,
|
|
|
|
completionActions = paramObject.oncompletion || "",
|
|
|
|
progressActions = paramObject.onprogress || "",
|
|
|
|
bindStatus = paramObject["bind-status"],
|
|
|
|
bindProgress = paramObject["bind-progress"],
|
|
|
|
method = paramObject.method || "GET",
|
|
|
|
HEADER_PARAMETER_PREFIX = "header-",
|
2023-01-13 11:34:25 +00:00
|
|
|
QUERY_PARAMETER_PREFIX = "query-",
|
2022-12-12 08:54:40 +00:00
|
|
|
PASSWORD_HEADER_PARAMETER_PREFIX = "password-header-",
|
2023-01-13 11:34:25 +00:00
|
|
|
PASSWORD_QUERY_PARAMETER_PREFIX = "password-query-",
|
2022-12-12 08:54:40 +00:00
|
|
|
CONTEXT_VARIABLE_PARAMETER_PREFIX = "var-",
|
|
|
|
requestHeaders = {},
|
|
|
|
contextVariables = {},
|
|
|
|
setBinding = function(title,text) {
|
|
|
|
if(title) {
|
|
|
|
self.wiki.addTiddler(new $tw.Tiddler({title: title, text: text}));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
if(url) {
|
|
|
|
setBinding(bindStatus,"pending");
|
|
|
|
setBinding(bindProgress,"0");
|
|
|
|
$tw.utils.each(paramObject,function(value,name) {
|
2023-01-13 11:34:25 +00:00
|
|
|
// Look for query- parameters
|
|
|
|
if(name.substr(0,QUERY_PARAMETER_PREFIX.length) === QUERY_PARAMETER_PREFIX) {
|
|
|
|
url = $tw.utils.setQueryStringParameter(url,name.substr(QUERY_PARAMETER_PREFIX.length),value);
|
|
|
|
}
|
2022-12-12 08:54:40 +00:00
|
|
|
// Look for header- parameters
|
|
|
|
if(name.substr(0,HEADER_PARAMETER_PREFIX.length) === HEADER_PARAMETER_PREFIX) {
|
|
|
|
requestHeaders[name.substr(HEADER_PARAMETER_PREFIX.length)] = value;
|
|
|
|
}
|
|
|
|
// Look for password-header- parameters
|
2023-01-13 11:34:25 +00:00
|
|
|
if(name.substr(0,PASSWORD_QUERY_PARAMETER_PREFIX.length) === PASSWORD_QUERY_PARAMETER_PREFIX) {
|
|
|
|
url = $tw.utils.setQueryStringParameter(url,name.substr(PASSWORD_QUERY_PARAMETER_PREFIX.length),$tw.utils.getPassword(value) || "");
|
|
|
|
}
|
|
|
|
// Look for password-query- parameters
|
2022-12-12 08:54:40 +00:00
|
|
|
if(name.substr(0,PASSWORD_HEADER_PARAMETER_PREFIX.length) === PASSWORD_HEADER_PARAMETER_PREFIX) {
|
|
|
|
requestHeaders[name.substr(PASSWORD_HEADER_PARAMETER_PREFIX.length)] = $tw.utils.getPassword(value) || "";
|
|
|
|
}
|
|
|
|
// Look for var- parameters
|
|
|
|
if(name.substr(0,CONTEXT_VARIABLE_PARAMETER_PREFIX.length) === CONTEXT_VARIABLE_PARAMETER_PREFIX) {
|
|
|
|
contextVariables[name.substr(CONTEXT_VARIABLE_PARAMETER_PREFIX.length)] = value;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
$tw.utils.httpRequest({
|
|
|
|
url: url,
|
|
|
|
type: method,
|
|
|
|
headers: requestHeaders,
|
|
|
|
data: paramObject.body,
|
|
|
|
callback: function(err,data,xhr) {
|
|
|
|
var headers = {};
|
|
|
|
$tw.utils.each(xhr.getAllResponseHeaders().split("\r\n"),function(line) {
|
2023-01-05 17:12:13 +00:00
|
|
|
var pos = line.indexOf(":");
|
|
|
|
if(pos !== -1) {
|
|
|
|
headers[line.substr(0,pos)] = line.substr(pos + 1).trim();
|
2022-12-12 08:54:40 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
setBinding(bindStatus,xhr.status === 200 ? "complete" : "error");
|
|
|
|
setBinding(bindProgress,"100");
|
|
|
|
var results = {
|
|
|
|
status: xhr.status.toString(),
|
|
|
|
statusText: xhr.statusText,
|
|
|
|
error: (err || "").toString(),
|
|
|
|
data: (data || "").toString(),
|
|
|
|
headers: JSON.stringify(headers)
|
|
|
|
};
|
|
|
|
$tw.rootWidget.invokeActionString(completionActions,undefined,undefined,$tw.utils.extend({},contextVariables,results));
|
|
|
|
// console.log("Back!",err,data,xhr);
|
|
|
|
},
|
|
|
|
progress: function(lengthComputable,loaded,total) {
|
|
|
|
if(lengthComputable) {
|
|
|
|
setBinding(bindProgress,"" + Math.floor((loaded/total) * 100))
|
|
|
|
}
|
|
|
|
$tw.rootWidget.invokeActionString(progressActions,undefined,undefined,{
|
|
|
|
lengthComputable: lengthComputable ? "yes" : "no",
|
|
|
|
loaded: loaded,
|
|
|
|
total: total
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.HttpClient = HttpClient;
|
|
|
|
|
|
|
|
/*
|
|
|
|
Make an HTTP request. Options are:
|
2013-03-17 15:28:49 +00:00
|
|
|
url: URL to retrieve
|
2018-08-23 12:13:49 +00:00
|
|
|
headers: hashmap of headers to send
|
2013-03-17 15:28:49 +00:00
|
|
|
type: GET, PUT, POST etc
|
2019-04-15 20:07:23 +00:00
|
|
|
callback: function invoked with (err,data,xhr)
|
2022-12-12 08:54:40 +00:00
|
|
|
progress: optional function invoked with (lengthComputable,loaded,total)
|
2017-03-17 13:41:17 +00:00
|
|
|
returnProp: string name of the property to return as first argument of callback
|
2013-03-17 15:28:49 +00:00
|
|
|
*/
|
|
|
|
exports.httpRequest = function(options) {
|
|
|
|
var type = options.type || "GET",
|
2020-03-30 14:24:05 +00:00
|
|
|
url = options.url,
|
2013-03-17 15:28:49 +00:00
|
|
|
headers = options.headers || {accept: "application/json"},
|
2020-08-17 17:44:36 +00:00
|
|
|
hasHeader = function(targetHeader) {
|
|
|
|
targetHeader = targetHeader.toLowerCase();
|
|
|
|
var result = false;
|
|
|
|
$tw.utils.each(headers,function(header,headerTitle,object) {
|
|
|
|
if(headerTitle.toLowerCase() === targetHeader) {
|
|
|
|
result = true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return result;
|
|
|
|
},
|
2021-08-17 08:56:52 +00:00
|
|
|
getHeader = function(targetHeader) {
|
|
|
|
return headers[targetHeader] || headers[targetHeader.toLowerCase()];
|
|
|
|
},
|
|
|
|
isSimpleRequest = function(type,headers) {
|
|
|
|
if(["GET","HEAD","POST"].indexOf(type) === -1) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
for(var header in headers) {
|
|
|
|
if(["accept","accept-language","content-language","content-type"].indexOf(header.toLowerCase()) === -1) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(hasHeader("Content-Type") && ["application/x-www-form-urlencoded","multipart/form-data","text/plain"].indexOf(getHeader["Content-Type"]) === -1) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
},
|
2017-03-17 13:41:17 +00:00
|
|
|
returnProp = options.returnProp || "responseText",
|
2013-03-17 15:28:49 +00:00
|
|
|
request = new XMLHttpRequest(),
|
|
|
|
data = "",
|
|
|
|
f,results;
|
|
|
|
// Massage the data hashmap into a string
|
|
|
|
if(options.data) {
|
|
|
|
if(typeof options.data === "string") { // Already a string
|
|
|
|
data = options.data;
|
|
|
|
} else { // A hashmap of strings
|
|
|
|
results = [];
|
|
|
|
$tw.utils.each(options.data,function(dataItem,dataItemTitle) {
|
|
|
|
results.push(dataItemTitle + "=" + encodeURIComponent(dataItem));
|
|
|
|
});
|
2020-03-30 14:24:05 +00:00
|
|
|
if(type === "GET" || type === "HEAD") {
|
|
|
|
url += "?" + results.join("&");
|
|
|
|
} else {
|
|
|
|
data = results.join("&");
|
|
|
|
}
|
2013-03-17 15:28:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// Set up the state change handler
|
|
|
|
request.onreadystatechange = function() {
|
|
|
|
if(this.readyState === 4) {
|
2014-09-24 14:19:23 +00:00
|
|
|
if(this.status === 200 || this.status === 201 || this.status === 204) {
|
2013-03-17 15:28:49 +00:00
|
|
|
// Success!
|
2017-03-17 13:41:17 +00:00
|
|
|
options.callback(null,this[returnProp],this);
|
2013-03-17 15:28:49 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Something went wrong
|
2019-04-15 20:07:23 +00:00
|
|
|
options.callback($tw.language.getString("Error/XMLHttpRequest") + ": " + this.status,null,this);
|
2013-03-17 15:28:49 +00:00
|
|
|
}
|
|
|
|
};
|
2022-12-12 08:54:40 +00:00
|
|
|
// Handle progress
|
|
|
|
if(options.progress) {
|
|
|
|
request.onprogress = function(event) {
|
|
|
|
console.log("Progress event",event)
|
|
|
|
options.progress(event.lengthComputable,event.loaded,event.total);
|
|
|
|
};
|
|
|
|
}
|
2013-03-17 15:28:49 +00:00
|
|
|
// Make the request
|
2020-03-30 14:24:05 +00:00
|
|
|
request.open(type,url,true);
|
2022-12-12 08:54:40 +00:00
|
|
|
// Headers
|
2013-03-17 15:28:49 +00:00
|
|
|
if(headers) {
|
|
|
|
$tw.utils.each(headers,function(header,headerTitle,object) {
|
|
|
|
request.setRequestHeader(headerTitle,header);
|
|
|
|
});
|
|
|
|
}
|
2020-08-17 17:44:36 +00:00
|
|
|
if(data && !hasHeader("Content-Type")) {
|
|
|
|
request.setRequestHeader("Content-Type","application/x-www-form-urlencoded; charset=UTF-8");
|
2013-03-17 15:28:49 +00:00
|
|
|
}
|
2021-08-17 08:56:52 +00:00
|
|
|
if(!hasHeader("X-Requested-With") && !isSimpleRequest(type,headers)) {
|
2018-07-18 15:54:43 +00:00
|
|
|
request.setRequestHeader("X-Requested-With","TiddlyWiki");
|
|
|
|
}
|
2022-12-12 08:54:40 +00:00
|
|
|
// Send data
|
2015-08-29 15:33:04 +00:00
|
|
|
try {
|
|
|
|
request.send(data);
|
|
|
|
} catch(e) {
|
2019-04-15 20:07:23 +00:00
|
|
|
options.callback(e,null,this);
|
2015-08-29 15:33:04 +00:00
|
|
|
}
|
2013-03-17 15:28:49 +00:00
|
|
|
return request;
|
|
|
|
};
|
|
|
|
|
2023-01-13 11:34:25 +00:00
|
|
|
exports.setQueryStringParameter = function(url,paramName,paramValue) {
|
|
|
|
var URL = $tw.browser ? window.URL : require("url").URL,
|
|
|
|
newUrl;
|
|
|
|
try {
|
|
|
|
newUrl = new URL(url);
|
|
|
|
} catch(e) {
|
|
|
|
}
|
|
|
|
if(newUrl && paramName) {
|
|
|
|
newUrl.searchParams.set(paramName,paramValue || "");
|
|
|
|
return newUrl.toString();
|
|
|
|
} else {
|
|
|
|
return url;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-03-17 15:28:49 +00:00
|
|
|
})();
|