1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-12-26 01:50:28 +00:00

Add basic support for traveltime.com isochrone API

This commit is contained in:
jeremy@jermolene.com 2022-12-12 08:54:40 +00:00
parent 15f266a01f
commit d2607489b6
10 changed files with 283 additions and 16 deletions

View File

@ -20,6 +20,11 @@ exports.before = ["story"];
exports.synchronous = true; exports.synchronous = true;
exports.startup = function() { exports.startup = function() {
// Install the HTTP client event handler
$tw.httpClient = new $tw.utils.HttpClient();
$tw.rootWidget.addEventListener("tm-http-request",function(event) {
$tw.httpClient.handleHttpRequest(event);
});
// Install the modal message mechanism // Install the modal message mechanism
$tw.modal = new $tw.utils.Modal($tw.wiki); $tw.modal = new $tw.utils.Modal($tw.wiki);
$tw.rootWidget.addEventListener("tm-modal",function(event) { $tw.rootWidget.addEventListener("tm-modal",function(event) {

View File

@ -3,7 +3,7 @@ title: $:/core/modules/utils/dom/http.js
type: application/javascript type: application/javascript
module-type: utils module-type: utils
Browser HTTP support HTTP support
\*/ \*/
(function(){ (function(){
@ -13,11 +13,99 @@ Browser HTTP support
"use strict"; "use strict";
/* /*
A quick and dirty HTTP function; to be refactored later. Options are: 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-",
PASSWORD_HEADER_PARAMETER_PREFIX = "password-header-",
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) {
// 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
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) {
var parts = line.split(":");
if(parts.length === 2) {
headers[parts[0].toLowerCase()] = parts[1].trim();
}
});
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:
url: URL to retrieve url: URL to retrieve
headers: hashmap of headers to send headers: hashmap of headers to send
type: GET, PUT, POST etc type: GET, PUT, POST etc
callback: function invoked with (err,data,xhr) callback: function invoked with (err,data,xhr)
progress: optional function invoked with (lengthComputable,loaded,total)
returnProp: string name of the property to return as first argument of callback returnProp: string name of the property to return as first argument of callback
*/ */
exports.httpRequest = function(options) { exports.httpRequest = function(options) {
@ -83,8 +171,16 @@ exports.httpRequest = function(options) {
options.callback($tw.language.getString("Error/XMLHttpRequest") + ": " + this.status,null,this); options.callback($tw.language.getString("Error/XMLHttpRequest") + ": " + this.status,null,this);
} }
}; };
// Handle progress
if(options.progress) {
request.onprogress = function(event) {
console.log("Progress event",event)
options.progress(event.lengthComputable,event.loaded,event.total);
};
}
// Make the request // Make the request
request.open(type,url,true); request.open(type,url,true);
// Headers
if(headers) { if(headers) {
$tw.utils.each(headers,function(header,headerTitle,object) { $tw.utils.each(headers,function(header,headerTitle,object) {
request.setRequestHeader(headerTitle,header); request.setRequestHeader(headerTitle,header);
@ -96,6 +192,7 @@ exports.httpRequest = function(options) {
if(!hasHeader("X-Requested-With") && !isSimpleRequest(type,headers)) { if(!hasHeader("X-Requested-With") && !isSimpleRequest(type,headers)) {
request.setRequestHeader("X-Requested-With","TiddlyWiki"); request.setRequestHeader("X-Requested-With","TiddlyWiki");
} }
// Send data
try { try {
request.send(data); request.send(data);
} catch(e) { } catch(e) {

View File

@ -0,0 +1,43 @@
caption: tm-http-request
created: 20220908161746341
modified: 20220908161746341
tags: Messages
title: WidgetMessage: tm-http-request
type: text/vnd.tiddlywiki
The ''tm-http-request'' message is used to make an HTTP request to a server.
It uses the following properties on the `event` object:
|!Name |!Description |
|param |Not used |
|paramObject |Hashmap of parameters (see below) |
The following parameters are used:
|!Name |!Description |
|method |HTTP method (eg "GET", "POST") |
|body |String data to be sent with the request |
|header-* |Headers with string values|
|password-header-* |Headers with values taken from the password store |
|var-* |Variables to be passed to the completion and progress handlers (without the "var-" prefix) |
|bind-status |Title of tiddler to which the status of the request ("pending", "complete", "error") should be bound |
|bind-progress |Title of tiddler to which the progress of the request (0 to 100) should be bound |
|oncompletion |Action strings to be executed when the request completes |
|onprogress |Action strings to be executed when progress is reported |
The following variables are passed to the completion handler:
|!Name |!Description |
|status |HTTP result status code (see [[MDN|https://developer.mozilla.org/en-US/docs/Web/HTTP/Status]]) |
|statusText |HTTP result status text |
|error |Error string |
|data |Returned data |
|headers |Response headers as a JSON object |
The following variables are passed to the progress handler:
|!Name |!Description |
|lengthComputable |Whether the progress loaded and total figures are valid - "yes" or "no" |
|loaded |Number of bytes loaded so far |
|total |Total number bytes to be loaded |

View File

@ -1,5 +1,6 @@
title: $:/plugins/geospatial/demo/features/us-states title: $:/plugins/geospatial/demo/features/us-states
type: application/json type: application/json
tags: $:/tags/GeoLayer
{"type":"FeatureCollection","features":[ {"type":"FeatureCollection","features":[
{"type":"Feature","id":"01","properties":{"name":"Alabama","density":94.65},"geometry":{"type":"Polygon","coordinates":[[[-87.359296,35.00118],[-85.606675,34.984749],[-85.431413,34.124869],[-85.184951,32.859696],[-85.069935,32.580372],[-84.960397,32.421541],[-85.004212,32.322956],[-84.889196,32.262709],[-85.058981,32.13674],[-85.053504,32.01077],[-85.141136,31.840985],[-85.042551,31.539753],[-85.113751,31.27686],[-85.004212,31.003013],[-85.497137,30.997536],[-87.600282,30.997536],[-87.633143,30.86609],[-87.408589,30.674397],[-87.446927,30.510088],[-87.37025,30.427934],[-87.518128,30.280057],[-87.655051,30.247195],[-87.90699,30.411504],[-87.934375,30.657966],[-88.011052,30.685351],[-88.10416,30.499135],[-88.137022,30.318396],[-88.394438,30.367688],[-88.471115,31.895754],[-88.241084,33.796253],[-88.098683,34.891641],[-88.202745,34.995703],[-87.359296,35.00118]]]}}, {"type":"Feature","id":"01","properties":{"name":"Alabama","density":94.65},"geometry":{"type":"Polygon","coordinates":[[[-87.359296,35.00118],[-85.606675,34.984749],[-85.431413,34.124869],[-85.184951,32.859696],[-85.069935,32.580372],[-84.960397,32.421541],[-85.004212,32.322956],[-84.889196,32.262709],[-85.058981,32.13674],[-85.053504,32.01077],[-85.141136,31.840985],[-85.042551,31.539753],[-85.113751,31.27686],[-85.004212,31.003013],[-85.497137,30.997536],[-87.600282,30.997536],[-87.633143,30.86609],[-87.408589,30.674397],[-87.446927,30.510088],[-87.37025,30.427934],[-87.518128,30.280057],[-87.655051,30.247195],[-87.90699,30.411504],[-87.934375,30.657966],[-88.011052,30.685351],[-88.10416,30.499135],[-88.137022,30.318396],[-88.394438,30.367688],[-88.471115,31.895754],[-88.241084,33.796253],[-88.098683,34.891641],[-88.202745,34.995703],[-87.359296,35.00118]]]}},

View File

@ -0,0 +1,11 @@
title: $:/plugins/tiddlywiki/geospatial/demo/maps
caption: Maps
tags: $:/tags/GeospatialDemo
! Map with Layers and Markers
<$geomap
markers="[all[tiddlers+shadows]tag[$:/tags/GeoMarker]]"
layers="[all[tiddlers+shadows]tag[$:/tags/GeoLayer]]"
/>

View File

@ -0,0 +1,92 @@
title: $:/plugins/tiddlywiki/geospatial/demo/traveltime
caption: Traveltime
tags: $:/tags/GeospatialDemo
\define completion-actions()
<$action-log/>
<$action-setfield $tiddler="$:/temp/_StatusCode" text=<<status>>/>
<$action-setfield $tiddler="$:/temp/_StatusText" text=<<statusText>>/>
<$action-setfield $tiddler="$:/temp/_Error" text=<<error>>/>
<$action-setfield $tiddler="$:/temp/_Result" text=<<data>>/>
<$action-setfield $tiddler="$:/temp/_Headers" text=<<headers>>/>
<$list filter="[<status>match[200]]" variable="ignore">
<$action-setfield $tiddler="$:/temp/_IsochroneLayer" text={{{ [<data>] }}} tags="$:/tags/GeoLayer"/>
</$list>
\end
\define progress-actions()
<$action-log message="In progress-actions"/>
<$action-log/>
\end
\define payload-source()
\rules only transcludeinline transcludeblock filteredtranscludeinline filteredtranscludeblock
{
"departure_searches": [
{
"id": "My first isochrone",
"coords": {
"lat": 51.507609,
"lng": -0.128315
},
"departure_time": "2021-09-27T08:00:00Z",
"travel_time": 3600,
"transportation": {
"type": "driving"
}
}
]
}
\end
\define get-traveltime-actions()
<$wikify name="payload" text=<<payload-source>>>
<$action-log $message="Making payload"/>
<$action-log/>
<$action-sendmessage
$message="tm-http-request"
url="https://api.traveltimeapp.com/v4/time-map"
method="POST"
header-accept="application/geo+json"
header-Content-Type="application/json"
password-header-X-Api-Key="traveltime-secret-key"
password-header-X-Application-Id="traveltime-application-id"
body=<<payload>>
var-context="Context string"
bind-status="$:/temp/plugins/tiddlywiki/geospatial/demo/traveltime/status"
bind-progress="$:/temp/plugins/tiddlywiki/geospatial/demo/traveltime/progress"
oncompletion=<<completion-actions>>
onprogress=<<progress-actions>>
/>
</$wikify>
\end
<$button actions=<<get-traveltime-actions>>>
Call ~TravelTime
</$button>
Status:
<pre><code><$text text={{$:/temp/plugins/tiddlywiki/geospatial/demo/traveltime/status}}/></code></pre>
Progress:
<pre><code><$text text={{$:/temp/plugins/tiddlywiki/geospatial/demo/traveltime/progress}}/></code></pre>
Response
~StatusCode:
<pre><code><$text text={{$:/temp/_StatusCode}}/></code></pre>
~StatusText:
<pre><code><$text text={{$:/temp/_StatusText}}/></code></pre>
Error:
<pre><code><$text text={{$:/temp/_Error}}/></code></pre>
Headers:
<pre><code><$text text={{$:/temp/_Headers}}/></code></pre>
Result:
<pre><code><$text text={{$:/temp/_Result}}/></code></pre>

View File

@ -2,5 +2,5 @@
"title": "$:/plugins/tiddlywiki/geospatial", "title": "$:/plugins/tiddlywiki/geospatial",
"name": "Geospatial Utilities", "name": "Geospatial Utilities",
"description": "Geospatial utilities", "description": "Geospatial utilities",
"list": "readme license" "list": "readme settings license"
} }

View File

@ -1,11 +1,5 @@
title: $:/plugins/tiddlywiki/geospatial/readme title: $:/plugins/tiddlywiki/geospatial/readme
! Examples ! Demos
!! Simple Map <<tabs tabsList:"[all[tiddlers+shadows]tag[$:/tags/GeospatialDemo]]" default:"$:/plugins/tiddlywiki/geospatial/demo/traveltime">>
<$geomap/>
!! Map with Markers
<$geomap markers="[all[tiddlers+shadows]tag[$:/tags/GeoMarker]]"/>

View File

@ -0,0 +1,15 @@
title: $:/plugins/tiddlywiki/geospatial/settings
tags: $:/tags/ControlPanel
caption: Geospatial Plugin
<div class="tc-control-panel">
! Geospatial Plugin Settings
Register for a free account at https://traveltime.com/ and copy and paste the secrets below:
~TravelTime Application ID: <$password name="traveltime-application-id"/>
~TravelTime Secret Key: <$password name="traveltime-secret-key"/>
</div>

View File

@ -56,6 +56,8 @@ GeomapWidget.prototype.renderMap = function(domNode) {
maxZoom: 19, maxZoom: 19,
attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>' attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
}).addTo(map); }).addTo(map);
// Disable Leaflet attribution
map.attributionControl.setPrefix("");
// Create default icon // Create default icon
const iconProportions = 365/560, const iconProportions = 365/560,
iconHeight = 50; iconHeight = 50;
@ -67,12 +69,18 @@ GeomapWidget.prototype.renderMap = function(domNode) {
}); });
// Add scale // Add scale
L.control.scale().addTo(map); L.control.scale().addTo(map);
// Add US states overlay // Add overlays
const layer = L.geoJSON($tw.utils.parseJSONSafe(self.wiki.getTiddlerText("$:/plugins/geospatial/demo/features/us-states"),[])).addTo(map); if(this.geomapLayerFilter) {
// Create markers $tw.utils.each(this.wiki.filterTiddlers(this.geomapLayerFilter),function(title) {
var tiddler = self.wiki.getTiddler(title);
if(tiddler) {
var layer = L.geoJSON($tw.utils.parseJSONSafe(tiddler.fields.text || "[]",[])).addTo(map);
}
});
}
// Add markers
if(this.geomapMarkerFilter) { if(this.geomapMarkerFilter) {
var titles = this.wiki.filterTiddlers(this.geomapMarkerFilter); $tw.utils.each(this.wiki.filterTiddlers(this.geomapMarkerFilter),function(title) {
$tw.utils.each(titles,function(title) {
var tiddler = self.wiki.getTiddler(title); var tiddler = self.wiki.getTiddler(title);
if(tiddler) { if(tiddler) {
var lat = $tw.utils.parseNumber(tiddler.fields.lat || "0"), var lat = $tw.utils.parseNumber(tiddler.fields.lat || "0"),
@ -89,6 +97,7 @@ GeomapWidget.prototype.renderMap = function(domNode) {
Compute the internal state of the widget Compute the internal state of the widget
*/ */
GeomapWidget.prototype.execute = function() { GeomapWidget.prototype.execute = function() {
this.geomapLayerFilter = this.getAttribute("layers");
this.geomapMarkerFilter = this.getAttribute("markers"); this.geomapMarkerFilter = this.getAttribute("markers");
}; };