How to get JavaScript response into HTML - javascript

I've got a HTML file which requires a token to access some data (from ArcGIS Online). A separate JavaScript file should call the service and get the token. The token then needs passing into the HTML file somehow, which is the bit I'm uncertain on.
In any case, code:
JavaScript file (GetAToken.js)
var request = require('request'); // npm install request
// generate a token with your client id and client secret
function getToken(callback)
{
request.post(
{
url: 'https://www.arcgis.com/sharing/rest/oauth2/token/',
json: true,
form:
{
'f': 'json',
'client_id': '<<MY CLIENT_ID>>',
'client_secret': '<<MY CLIENT_SECRET>>',
'grant_type': 'client_credentials',
'expiration': '1440'
}
}, function (error, response, body)
{
console.log(body.access_token);
callback(body.access_token);
});
}
And the relevant bits from the HTML
<script src="GetAToken.js"></script>
</head>
<body onload="getToken()">
<div class="embed-container">
<iframe width="500"
height="400"
frameborder="0"
scrolling="no"
marginheight="0"
marginwidth="0"
title="Test Map" src="//MyMap.maps.arcgis.com/apps/webappviewer/index.html?id=LongMapID?token=I_Need_My_Token_Here&extent=1,-1,1,-1&zoom=true&scale=true&search=true&searchextent=true&legend=true&basemap_gallery=true&disable_scroll=true&theme=light">
</iframe>
</div>
</body>
</html>
If you look within the div in the HTML, that's where I need my token to go. The JavaScript apparently returns a value called access_token, and is written using node.js
EDIT
New GetAToken.js
const request = require('request'); // npm install request
const express = require('express');
const app = express();
// generate a token with your client id and client secret
//function getToken(callback)
app.get('/GetAToken', (req, res) => {
request.post(
{
url: 'https://www.arcgis.com/sharing/rest/oauth2/token/',
json: true,
form:
{
'f': 'json',
'client_id': '<<MY_CLIENT_ID>>',
'client_secret': '<<MY_CLIENT_SECRET>>',
'grant_type': 'client_credentials',
'expiration': '1440'
}
}, function (error, response, body) {
console.log(body.access_token);
callback(body.access_token);
});
});
app.listen(80);
Updated HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>Title</title>
<link href="https://esri.github.io/calcite-bootstrap/assets/css/calcite-bootstrap-open.min.css" rel="stylesheet">
<style>
.footer
{
height: 6.25rem;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="https://esri.github.io/calcite-bootstrap/assets/js/bootstrap.min.js"></script>
<script src="https://js.arcgis.com/3.17/"></script>
<script src="GetAToken.js"></script>
<script type="text/javascript">
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function()
{
if(xhttp.readyState === 4 && xhttp.status === 200)
{
var responseJSON = JSON.parse(xhttp.responseText);
var token = responseJSON.token;
alert(token);
}
}
xhttp.open("GET", "GetAToken", true);
xhttp.send();
</script>
</head>
<body>
<style>
.embed-container
{
position: relative;
padding-bottom: 50%;
height: 0;
max-width: 100%;
}
.embed-container iframe, .embed-container object, .embed-container iframe
{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
small
{
position: absolute;
z-index: 40;
bottom: 0;
margin-bottom: -15px;
}
</style>
<div class="embed-container">
<iframe width="500"
height="400"
frameborder="0"
scrolling="no"
marginheight="0"
marginwidth="0"
title="Test Map" src="//MyMap.maps.arcgis.com/apps/webappviewer/index.html?id=MyLongID&extent=1,-1,1,-1&zoom=true&scale=true&search=true&searchextent=true&legend=true&basemap_gallery=true&disable_scroll=true&theme=light">
</iframe>
</div>
</body>
</html>

You're going to want to make the response of that request to arcgis available to the client somehow. Here's an example using express:
const request = require('request'); // npm install request
const express = require('express'); // npm install express
const app = express();
app.get('/get-a-token', (req, res) =>
{
request.post(
{
url: 'https://www.arcgis.com/sharing/rest/oauth2/token/',
json: true,
form:
{
'f': 'json',
'client_id': '<<MY CLIENT_ID>>',
'client_secret': '<<MY CLIENT_SECRET>>',
'grant_type': 'client_credentials',
'expiration': '1440'
}
}, function (error, response, body)
{
console.log(body.access_token);
res.json({token: body.access_token});
});
});
app.listen(80);
Then on the client, you could do something like this to get the value from the server:
<script type="text/javascript">
// You may want to move this to another file..
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
if (xhttp.readyState === 4 && xhttp.status === 200) {
var responseJSON = JSON.parse(xhttp.responseText);
var token = responseJSON.token;
var iframe = document.querySelectorAll('iframe')[0]
iframe.src = "//MyMap.maps.arcgix.com/apps/webappviewer/index.html?id=LongMapID?token=" + token + "&extent=1,-1,1,-1&zoom=true&scale=true&search=true&searchextent=true&legend=true&basemap_gallery=true&disable_scroll=true&theme=light"
}
}
xhttp.open("GET", "http://yournodeserver.com/get-a-token", true);
xhttp.send();
</script>
You might want to do something to protect the /get-a-token route from being accessed by sites other than yours.
If you are serving your html file with node/express too then you could solve this by inserting the token to the html before serving it to the client instead

Related

Getting node.js output(from Mysql) to Javascript(client-side)

Title says it all I have a freelance project( first one and I promised it would be done by now) and I still can't figure it out. I just want the Name column from my MySQL database (that is being shown on my node.js when I console.log the results) into a JavaScript file. ( I feel like this is a simple solution staring me in the face and I can't figure it out)
**CLIENT SIDE**
var results = ("server.js")
var nodes = new vis.DataSet([
{label: "Pop"},
{label: "Alternative"},
{label: "Rock"},
{label: "Jazz"},
{label: "Hits"},
{label: "Dance"},
{label: "Metal"},
{label: "Experimental"},
{label: "Rap"},
{label: "Electronic"},
]);
var edges = new vis.DataSet();
var container = document.getElementById('bubbles');
var data = {
nodes: nodes,
edges: edges
};
var options = {
nodes: {borderWidth:0,shape:"circle",color:{background:'#F92C55', highlight:{background:'#F92C55', border: '#F92C55'}},font:{color:'#fff'}},
physics: {
stabilization: false,
minVelocity: 0.01,
solver: "repulsion",
repulsion: {
nodeDistance: 40
}
}
};
var network = new vis.Network(container, data, options);
// Events
network.on("click", function(e) {
if (e.nodes.length) {
var node = nodes.get(e.nodes[0]);
// Do something
nodes.update(node);
}
});
container.on("mouse-wheel", function(event) {
// prevents zooming with the mouse-wheel event
event.stopPropagation();
});
console.log (results.name)
**SERVER SIDE**
var mysql = require('mysql');
var express = require('express');
const path = require('path')
// var index = require("index.html")
app = express();
app.get('/',(request,response)=>{
;
response.sendFile(path.join(__dirname, '/index.html'));
app.use(express.static((__dirname)));
});
//Binding the server to a port(3000)
app.listen(3000,()=>console.log("express server started at port 3000"));
var con = mysql.createConnection({
host: "localhost",
user: "Nate",
password: "Black1022!",
database: "new_schema"
});
con.connect(function(err) {
if (err) throw err;
console.log("Connected!");
con.query("SELECT Name FROM test", function (err, result) {
if (err) throw err;
console.log(result);
module.exports = result;
});
});
#bubbles {
position: absolute;
width: 100vw;
height: 100vh;
top: 0;
left: 0;
}
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>CodePen - Apple Music Style Bubble UI</title>
<link rel="stylesheet" type="text/css" href="./style.css">
</head>
<body>
<!-- partial:index.partial.html -->
<div id="bubbles"></div>
<!-- partial -->
<script src='https://cdnjs.cloudflare.com/ajax/libs/vis/4.16.1/vis.min.js'></script>
<script
src="https://code.jquery.com/jquery-3.6.0.js"
integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk="
crossorigin="anonymous"></script>
<script src="./script.js"></script>
</body>
</html>
I would just remake the whole thing using MVC architecture but I feel like that would take too much time and then I feel like I would be at a loss again anyways.
edit: The "label" part will be replace by the names in the database.
Thanks for the help in advance!!

"events.js: 377" occurs when I am running the app.js as a node server on Hyper cmd

"events.js: 377" occurs when I am running the app.js as a node server on Hyper. Please also mention the full descriptive meaning of this "events.js: 377" and how we can figure it out this.
I am coding a Signup Newsletter and the code related to .html, .js and .css files are attached below.
I have many times changed the pattern of my code but I am not able to run the app.js as it is getting crashed every time.
If anybody knows about the solution then please answer.
Given below is link of the image of the error that is shown on the hyper when I run app.js as a server and it gets crashed.
The below given code is of app.js :
const express = require("express");
const https = require("https");
const bodyParser = require("body-parser");
const request = require("request");
const app = express();
app.use(express.static("public"));
app.use(bodyParser.urlencoded({extended: true}));
app.get("/", function(req, res){
res.sendFile(__dirname + "/signup.html");
})
app.post("/", function(req, res){
const firstName = req.body.fName;
const lastName = req.body.lName;
const email = req.body.email;
const data = {
members:[
{
email_address: email,
status: "subscribed",
merge_fields: {
FNAME: firstName,
LNAME: lastName
}
}
]
}
});
const jsonData = JSON.stringify("data");
const url = "https://us5.api.mailchimp.com/3.0/lists/21760b9a041";
const options = {
method: "POST",
auth: "xxxx"
}
const requestt = https.request(url, options, function(response) {
response.on("data", function(data){
console.log(JSON.parse(data));
})
requestt.write(jsonData);
requestt.end
});
app.listen("3000", function(){
console.log("Server is running on port 3000");
});
The given below code is of signup.html :
***<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors">
<meta name="generator" content="Hugo 0.88.1">
<title>Signin Template · Bootstrap v5.1</title>
<link rel="canonical" href="https://getbootstrap.com/docs/5.1/examples/sign-in/">
<!-- When Internet will be ON only then this link of Bootstrap will be applied to your website. -->
<!-- Bootstrap core CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.1.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-uWxY/CJNBR+1zjPWmfnSnVxwRheevXITnMqoEIeG1LJrdI0GlVs/9cVSyPYXdcSF" crossorigin="anonymous">
<!-- Favicons -->
<link rel="apple-touch-icon" href="/docs/5.1/assets/img/favicons/apple-touch-icon.png" sizes="180x180">
<link rel="icon" href="/docs/5.1/assets/img/favicons/favicon-32x32.png" sizes="32x32" type="image/png">
<link rel="icon" href="/docs/5.1/assets/img/favicons/favicon-16x16.png" sizes="16x16" type="image/png">
<link rel="manifest" href="/docs/5.1/assets/img/favicons/manifest.json">
<link rel="mask-icon" href="/docs/5.1/assets/img/favicons/safari-pinned-tab.svg" color="#7952b3">
<link rel="icon" href="/docs/5.1/assets/img/favicons/favicon.ico">
<meta name="theme-color" content="#7952b3">
<!-- Custom styles for this template -->
<link href="css/styles.css" rel="stylesheet">
</head>
<body class="text-center">
<form class="form-signin" action="/" method="POST">
<img class="mb-4" src="Image/lab.jpg" alt="" width="72" height="57">
<h1 class="h3 mb-3 fw-normal">Signup to my Newsletter.</h1>
<input type="text" name = fName class="form-control middle" id="inputPassword" placeholder="First Name" required>
<input type="text" name = lName class="form-control top" id="inputEmail" placeholder="Last Name" required autofocus>
<input type="email"name = email class="form-control bottom" placeholder="Email" required>
<button class="w-100 btn btn-lg btn-primary btn-block" type="submit">Sign Me Up!</button>
<p class="mt-5 mb-3 text-muted">© 2017–2021</p>
</form>
</body>
</html>***
The given below code is of styles.css :
body {
height: 100%;
}
body {
display: flex;
align-items: center;
padding-top: 40px;
padding-bottom: 40px;
background-color: #f5f5f5;
}
.form-signin {
width: 100%;
max-width: 330px;
padding: 15px;
margin: auto;
}
.form-signin .checkbox {
font-weight: 400;
}
.form-signin .form-floating:focus-within {
z-index: 2;
}
.top {
margin-bottom: 2px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
.middle {
border-radius: 0;
margin-bottom: 2px;
}
.bottom{
margin-bottom: 10px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
Looks like you are very new to javascript. I would recommend formatting your code a bit better and you might catch errors like these a bit easier.
The exact cause of your error is that you are getting an error when making your API request to mailchimp and you do not have an error handler. Secondarily the reason you are likely getting that error is due to the fact that you are just sending them the literal string 'data' and nothing else.
Your code when properly formatted looks like this:
app.post("/", function(req, res) {
const firstName = req.body.fName;
const lastName = req.body.lName;
const email = req.body.email;
const data = {
members: [{
email_address: email,
status: "subscribed",
merge_fields: {
FNAME: firstName,
LNAME: lastName
}
}]
}
});
const jsonData = JSON.stringify("data");
const url = "https://us5.api.mailchimp.com/3.0/lists/21760b9a041";
const options = {
method: "POST",
auth: "xxxx"
};
const requestt = https.request(url, options, function(response) {
response.on("data", function(data) {
console.log(JSON.parse(data));
});
requestt.write(jsonData);
requestt.end
});
Note that your post to mailchimp is outside of your app.post callback handler. Due to that code's location it will execute the moment you run your code. First you need to move this code inside your route handler.
app.post("/", function(req, res) {
const firstName = req.body.fName;
const lastName = req.body.lName;
const email = req.body.email;
const data = {
members: [{
email_address: email,
status: "subscribed",
merge_fields: {
FNAME: firstName,
LNAME: lastName
}
}]
};
const jsonData = JSON.stringify("data");
const url = "https://us5.api.mailchimp.com/3.0/lists/21760b9a041";
const options = {
method: "POST",
auth: "xxxx"
};
const requestt = https.request(url, options, function(response) {
response.on("data", function(data) {
console.log(JSON.parse(data));
});
requestt.write(jsonData);
requestt.end
});
});
This will prevent your code from executing the moment you run your code.
Secondly you need to add an error handler to your request method so that if an error occurs you do not cause an unhandled error to crash your program. That is what is causing you to see the Unhandled 'error' event message right below events:377.
const requestt = https.request(url, options, function(response) {
response.on("data", function(data) {
console.log(JSON.parse(data));
});
response.on("error", function(error) {
console.error('Error from mailchimp', error);
});
requestt.write(jsonData);
requestt.end
});
Lastly, the actual error you are seeing is socket hang up. This error is generally a network error meaning that your client's TCP requests to the target server were dropped/rejected or otherwise terminated before they could be completed. This may be a result of not actually calling the end function when making your request. Specifically you have requestt.end, which is a function, but you are not calling it, should be requestt.end();. If this doesn't resolve it then you may want to check the network connectivity of the computer you are running this code on.
Another error I noticed in your code:
const jsonData = JSON.stringify("data");
You probably mean to send the data object that you are creating, in that case you do not want to wrap data in quotes as it is a variable name, not a string. Instead it should be:
const jsonData = JSON.stringify(data);
I also experienced the same issue and I got this fixed. The root cause at my end was that port number I used was engaged/used at another program.
app.listen("3000", function(){...
Once I closed the other program that was using this port number, my program worked fine.

firebase auth signInWithCustomToken method not working in iPhone

I am developing one PWA of the ionic capacitor and there is a requirement to load another website in an iframe so from one of the ionic app component I called another site.
iframe.page.html
<ion-header>
<ion-toolbar>
<ion-buttons slot="end" (click)="goBack()">
Close
<ion-icon slot="icon-only" name="close-outline"></ion-icon>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<iframe #iframe height="100%" width="100%" title="test"></iframe>
</ion-content>
iframe.page.ts
export class IframePage implements OnInit, AfterViewInit {
userId = localStorage.getItem('userId');
gameData: any;
gameUrl: any;
docId;
#ViewChild('iframe') iframe: ElementRef;
constructor(private navCtrl: NavController,
private authSrv: AuthService,
private commanSrv: CommannService,
private router:Router) {
firebase.analytics().logEvent('Web_game');
this.iframe.nativeElement.setAttribute('src', 'anotherdomain.com?UID=sadsajdhsakjhdasjhkjsd');
window.addEventListener('message', (event) => {
console.log(event.data);
if (event.data.res === 'win') {
// let routename = commanSrv.getLastRoute();
// navCtrl.navigateForward(routename || 'home_tab');
this.router.navigate(['arcade-outcome', { type: 'arcade_win' }]);
} else if (event.data.res === 'loss'){
this.router.navigate(['arcade-outcome', { type: 'arcade_lose' }]);
} else {
// if (event.data === 'closed') {
let routename = commanSrv.getLastRoute();
navCtrl.navigateBack(routename || 'home_tab');
// }
}
});
}
}
And now
another site is made up of HTML and javascript and here is code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/png" href="assets/icon/favicon.png" />
<link rel="stylesheet" type="text/css" href="./assets/css/custom.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.4.3/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.4.3/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.4.3/firebase-firestore.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.4.3/firebase-database.js"></script>
<script src="scripts/firebase.js"></script>
<script type="text/javascript" src="scripts/index.js"></script>
<title>HTML 5 game application</title>
</head>
<body>
<p>Loader screen</p>
<div class="loader"></div>
<script>
$(document).ready(function(){
const serverUrl = 'https://us-central1-domain.cloudfunctions.net/';
var token;
var xhttp = new XMLHttpRequest();
var gamePlayData;
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const userId = urlParams.get('UID')
const gamePlay = urlParams.get('gameplay')
const mode = urlParams.get('mode')
function getUserDetails(token) {
console.log("getUserDetails start token ", token);
console.log(firebase);
firebase.auth().signInWithCustomToken(token).then((userCredential) => { // execution stop from here.
console.log("getUserDetails end");
var user = userCredential.user;
if (user !== null) {
window.location.replace(`anotherdomain.com/start.html?id=${userId}&gamePlay=${gamePlay}`);
}
})
.catch((error) => {
console.log('error in catch: ', error);
var errorCode = error.code;
var errorMessage = error.message;
// window.history.back();
alert('User is not logged in.')
// parent.postMessage('closed', '*');
});
}
function checkAuth() {
console.log("ajax call start");
$.ajax({
type: "POST",
url: serverUrl + 'checkAuthStatus',
data: { uid: userId },
success: function (response) {
console.log("ajax call end");
//if request if made successfully then the response represent the data
getUserDetails(response.result);
},
error: function (err) {
console.log(err);
}
});
}
checkAuth();
});
</script>
From the line
firebase.auth().signInWithCustomToken(token).then(() => {
Execution cancelled and iframe got closed , window.addEventListener('message', (event) => { got called navigated to ionic application back.
The surprising is, this issue only occurs in iPhone whereas for android it is working fine.
When navigating to another domain site 3rd time then it working and got the success in firebase.auth().signInWithCustomToken(token).then((userCredential) => { .
Please help me.

Ditto HTTP API server sent events CORS error

I installed Hono+Ditto using helm-charts, as it is described in cloud2edge.
That means Hono+Ditto is running inside a minikube on my PC.
I also created a connection, policy, and a device. So far everything works fine.
In the next step, I just wrote a simple "frond-end" to fetch the thing state from Ditto-HTTP-API.
As long as I fetch the thing state manually by the mean of fetch-API everything is fine. But as soon as I try to use the SSE (Eventsource) I get the following CORS error:
index.html:1 Access to resource at 'http://192.168.99.100:32084/api/2/things/de.iot1:dev1' from
origin 'http://localhost:63342' has been blocked by CORS policy: The value of the
'Access-Control-Allow-Origin' header in the response must not be the wildcard '*'
when the request's credentials mode is 'include'.
I am just struggling with this error since yesterday and none of the answers regarding CORS-errors I found on the internet worked :(.
How can I communicate with Ditto from my PC using Eventsource without getting CORs-error?
Below is my simple front-end:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
{
box-sizing: border-box;
}
.container {
display: grid;
}
.row:after {
content: "";
display: table;
clear: both;
}
</style>
</head>
<body>
<div class="container">
<div class="row">
<label for="selector">Choose update strategy:
<select name="method" id="selector">
<option value="auto">Autorefresh</option>
<option value="SSE">SSE</option>
</select>
</label>
</div>
<div class="row">
<label for="dev"><h3>Device state:</h3></label>
</div>
<div class="row">
<textarea id="dev" name="dev-data" rows="20" cols="50"></textarea>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"
integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0="
crossorigin="anonymous"></script>
<script>
const baseUrl = "http://192.168.99.100:32084"; // Ditto IP:PORT
const username = "ditto";
const password = "ditto";
const interval = 1000;
const thingId = "de.iot1:dev1";
const thingUrl = `${baseUrl}/api/2/things/${thingId}`;
let intervalId;
let eventSource = null
function requestData(url) {
var headers;
headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Authorization', 'Basic ' + btoa(`${username}:${password}`));
init = {
method: 'GET',
headers: headers,
};
var request = new Request(url);
return fetch(request, init)
.then(function (response) {
if (response.ok) {
return response;
}
throw response;
})
}
function updateDeviceState(data) {
$('#dev').val(JSON.stringify(data, null, 2));
}
function onRefresh() {
requestData(thingUrl)
.then(response => {
for (var pair of response.headers.entries()) {
console.log(pair[0] + ': ' + pair[1]);
}
return response.json()
})
.then(data => { updateDeviceState(data) });
}
function enableAutoRefresh(enabled=true) {
if (enabled) {
intervalId = setInterval(() => { onRefresh() }, interval);
} else {
clearInterval(intervalId);
}
}
function enableEventSource(enabled=true) {
if (enabled) {
eventSource = new EventSource(thingUrl, {withCredentials: true})
eventSource.addEventListener('message', (e) => { console.log(e) })
} else if (eventSource != null) {
eventSource.removeEventListener('message', this.eventListener)
eventSource.close();
eventSource = null;
}
}
function applyUpdateStrategy() {
let val = $('#selector').val();
let autoRefreshEnabled = val.includes('auto');
enableAutoRefresh(autoRefreshEnabled);
enableEventSource(!autoRefreshEnabled);
}
$('#selector').on('change', () => { applyUpdateStrategy() })
applyUpdateStrategy()
</script>
</body>
</html>
Thanks!
Thank you for reaching out.
You found a bug which was already fixed the Ditto nginx configuration, however not yet applied to the "packages" project.
I created a PR to fix this, so this should be fixed in the next Helm version of the Ditto chart:
https://github.com/eclipse/packages/pull/193
This question would have been better placed on GitHub as issue - but you could of course not have known that this was a bug before.

<audio> tag not working with IBM Watson Text-to-Speech Node.js api

I'm building a chatbot using IBM Watson on Node.js and I'm having some trouble integrating the text-to-speech api. I've cloned an example on github that works when I run it, but when I try to implement it into my code, no audio plays.
app.js(server side):
/**
* Copyright 2015 IBM Corp. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
require( 'dotenv' ).config( {silent: true} );
const TextToSpeechV1 = require('watson-developer-cloud/text-to-speech/v1');
var express = require( 'express' ); // app server
var bodyParser = require( 'body-parser' ); // parser for post requests
var Watson = require( 'watson-developer-cloud/conversation/v1' ); // watson sdk
// The following requires are needed for logging purposes
var uuid = require( 'uuid' );
var vcapServices = require( 'vcap_services' );
var basicAuth = require( 'basic-auth-connect' );
// The app owner may optionally configure a cloudand db to track user input.
// This cloudand db is not required, the app will operate without it.
// If logging is enabled the app must also enable basic auth to secure logging
// endpoints
var cloudantCredentials = vcapServices.getCredentials( 'cloudantNoSQLDB' );
var cloudantUrl = null;
if ( cloudantCredentials ) {
cloudantUrl = cloudantCredentials.url;
}
cloudantUrl = cloudantUrl || process.env.CLOUDANT_URL; // || '<cloudant_url>';
var logs = null;
var app = express();
// set up routes
var token = require('./routes/token');
app.use('/token', token);
// Bootstrap application settings
app.use( express.static( './public' ) ); // load UI from public folder
app.use( bodyParser.json() );
// Create the service wrapper
var conversation = new Watson( {
// If unspecified here, the CONVERSATION_USERNAME and CONVERSATION_PASSWORD env properties will be checked
// After that, the SDK will fall back to the bluemix-provided VCAP_SERVICES environment property
// username: '<username>',
// password: '<password>',
username: process.env.ASSISTANT_USERNAME || '<username>',
password: process.env.ASSISTANT_PASSWORD || '<password>',
url: 'https://gateway.watsonplatform.net/conversation/api',
version_date: '2016-09-20',
version: 'v1'
} );
const textToSpeech = new TextToSpeechV1({
username: process.env.TEXT_TO_SPEECH_USERNAME,
password: process.env.TEXT_TO_SPEECH_PASSWORD,
version: 'v1'
// If unspecified here, the TEXT_TO_SPEECH_USERNAME and
// TEXT_TO_SPEECH_PASSWORD env properties will be checked
// After that, the SDK will fall back to the bluemix-provided VCAP_SERVICES environment property
// username: '<username>',
// password: '<password>',
});
/************************************************
* Conversation services
************************************************/
// Endpoint to be call from the client side
app.post( '/api/message', function(req, res) {
var workspace = process.env.WORKSPACE_ID || '<workspace-id>';
if ( !workspace || workspace === '<workspace-id>' ) {
return res.json( {
'output': {
'text': 'The app has not been configured with a <b>WORKSPACE_ID</b> environment variable. Please refer to the ' +
'README documentation on how to set this variable. <br>' +
'Once a workspace has been defined the intents may be imported from ' +
'here in order to get a working application.'
}
} );
}
var payload = {
workspace_id: workspace,
context: {},
input: {}
};
if ( req.body ) {
if ( req.body.input ) {
payload.input = req.body.input;
}
if ( req.body.context ) {
// The client must maintain context/state
payload.context = req.body.context;
}
}
// Send the input to the conversation service
conversation.message( payload, function(err, data) {
if ( err ) {
return res.status( err.code || 500 ).json( err );
}
return res.json( updateMessage( payload, data ) );
} );
} );
/**
* Updates the response text using the intent confidence
* #param {Object} input The request to the Conversation service
* #param {Object} response The response from the Conversation service
* #return {Object} The response with the updated message
*/
function updateMessage(input, response) {
var responseText = null;
var id = null;
if ( !response.output ) {
response.output = {};
} else {
if ( logs ) {
// If the logs db is set, then we want to record all input and responses
id = uuid.v4();
logs.insert( {'_id': id, 'request': input, 'response': response, 'time': new Date()});
}
return response;
}
if ( response.intents && response.intents[0] ) {
var intent = response.intents[0];
// Depending on the confidence of the response the app can return different messages.
// The confidence will vary depending on how well the system is trained. The service will always try to assign
// a class/intent to the input. If the confidence is low, then it suggests the service is unsure of the
// user's intent . In these cases it is usually best to return a disambiguation message
// ('I did not understand your intent, please rephrase your question', etc..)
if ( intent.confidence >= 0.75 ) {
responseText = 'I understood your intent was ' + intent.intent;
} else if ( intent.confidence >= 0.5 ) {
responseText = 'I think your intent was ' + intent.intent;
} else {
responseText = 'I did not understand your intent';
}
}
response.output.text = responseText;
if ( logs ) {
// If the logs db is set, then we want to record all input and responses
id = uuid.v4();
logs.insert( {'_id': id, 'request': input, 'response': response, 'time': new Date()});
}
return response;
}
if ( cloudantUrl ) {
// If logging has been enabled (as signalled by the presence of the cloudantUrl) then the
// app developer must also specify a LOG_USER and LOG_PASS env vars.
if ( !process.env.LOG_USER || !process.env.LOG_PASS ) {
throw new Error( 'LOG_USER OR LOG_PASS not defined, both required to enable logging!' );
}
// add basic auth to the endpoints to retrieve the logs!
var auth = basicAuth( process.env.LOG_USER, process.env.LOG_PASS );
// If the cloudantUrl has been configured then we will want to set up a nano client
var nano = require( 'nano' )( cloudantUrl );
// add a new API which allows us to retrieve the logs (note this is not secure)
nano.db.get( 'car_logs', function(err) {
if ( err ) {
console.error(err);
nano.db.create( 'car_logs', function(errCreate) {
console.error(errCreate);
logs = nano.db.use( 'car_logs' );
} );
} else {
logs = nano.db.use( 'car_logs' );
}
} );
// Endpoint which allows deletion of db
app.post( '/clearDb', auth, function(req, res) {
nano.db.destroy( 'car_logs', function() {
nano.db.create( 'car_logs', function() {
logs = nano.db.use( 'car_logs' );
} );
} );
return res.json( {'message': 'Clearing db'} );
} );
// Endpoint which allows conversation logs to be fetched
app.get( '/chats', auth, function(req, res) {
logs.list( {include_docs: true, 'descending': true}, function(err, body) {
console.error(err);
// download as CSV
var csv = [];
csv.push( ['Question', 'Intent', 'Confidence', 'Entity', 'Output', 'Time'] );
body.rows.sort( function(a, b) {
if ( a && b && a.doc && b.doc ) {
var date1 = new Date( a.doc.time );
var date2 = new Date( b.doc.time );
var t1 = date1.getTime();
var t2 = date2.getTime();
var aGreaterThanB = t1 > t2;
var equal = t1 === t2;
if (aGreaterThanB) {
return 1;
}
return equal ? 0 : -1;
}
} );
body.rows.forEach( function(row) {
var question = '';
var intent = '';
var confidence = 0;
var time = '';
var entity = '';
var outputText = '';
if ( row.doc ) {
var doc = row.doc;
if ( doc.request && doc.request.input ) {
question = doc.request.input.text;
}
if ( doc.response ) {
intent = '<no intent>';
if ( doc.response.intents && doc.response.intents.length > 0 ) {
intent = doc.response.intents[0].intent;
confidence = doc.response.intents[0].confidence;
}
entity = '<no entity>';
if ( doc.response.entities && doc.response.entities.length > 0 ) {
entity = doc.response.entities[0].entity + ' : ' + doc.response.entities[0].value;
}
outputText = '<no dialog>';
if ( doc.response.output && doc.response.output.text ) {
outputText = doc.response.output.text.join( ' ' );
}
}
time = new Date( doc.time ).toLocaleString();
}
csv.push( [question, intent, confidence, entity, outputText, time] );
} );
res.csv( csv );
} );
} );
}
/************************************************
* Text-to-Speech services
************************************************/
/**
* Pipe the synthesize method
*/
app.get('/api/synthesize', (req, res, next) => {
const transcript = textToSpeech.synthesize(req.query);
transcript.on('response', (response) => {
if (req.query.download) {
if (req.query.accept && req.query.accept === 'audio/wav') {
response.headers['content-disposition'] = 'attachment; filename=transcript.wav';
} else {
response.headers['content-disposition'] = 'attachment; filename=transcript.ogg';
}
}
});
transcript.on('error', next);
transcript.pipe(res);
});
// Return the list of voices
app.get('/api/voices', (req, res, next) => {
textToSpeech.voices(null, (error, voices) => {
if (error) {
return next(error);
}
res.json(voices);
});
});
require('./config/error-handler')(app);
module.exports = app;
tts-custom.js(client side):
function text_to_speech(text) {
console.log(text);
// text = '<express-as type="GoodNews">'+text+'</express-as>';
text = encodeURIComponent(text);
console.log(text);
$.ajax({
method: 'GET',
url: '/api/synthesize?voice=en-US_AllisonVoice&download=true&text='+text,
dataType: 'native',
xhrFields: {
responseType: 'blob'
},
success: function(blob) {
var url = (URL || webkitURL).createObjectURL(blob);
$('#audio').attr('src', url);
$('#audio').attr('type', 'audio/ogg;codecs=opus');
}
})
}
chat.html:
<html>
<head>
<title> Chat </title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta property="og:image" content="conversation.svg" />
<meta property="og:title" content="Conversation Chat Simple" />
<meta property="og:description" content="Sample application that shows how to use the Watson Assistant API to identify user intents"
/>
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="css/app.css">
<!-- <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.1/css/font-awesome.min.css" /> -->
<link rel="stylesheet" href="css/speech-input.css">
<script src="js/conversation.js"></script>
<!-- <script src="js/bundle.js"></script>
-->
<style type="text/css">
#result{
height: 200px;
border: 1px solid #ccc;
padding: 10px;
box-shadow: 0 0 10px 0 #bbb;
margin-bottom: 30px;
font-size: 14px;
line-height: 25px;
font-family: verdana;
}
button{
font-size: 20px;
position: absolute;
bottom: 10%;
right: 10%;
}
</style>
<style>
.mystyle {
display:none;
}
</style>
<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.3.min.js" integrity="sha256-aaODHAgvwQW1bFOGXMeX+pC4PZIPsvn2h1sArYOhgXQ=" crossorigin="anonymous"></script>
<script src="js/tts-custom.js"></script>
</head>
<body>
<!-- <div id="contentParent" class="responsive-columns-wrapper"> -->
<div id="view-change-button" class="button" onclick="hideChat(this)">
<img class="option full" src="../img/Chat Button.png">
<img class="option not-full" src="../img/Code Button.png">
</div>
<div id="chat-column-holder" class="responsive-column content-column">
<div class="chat-column">
<div id="scrollingChat"></div>
<label for="textInput" class="inputOutline">
<input id="textInput" class= "text" lang="es" placeholder="Type something" type="text" onkeydown="ConversationPanel.inputKeyDown(event, this, false)">
<!-- add this to class in textInput to put back google mic:, speech-input -->
</label>
<audio autoPlay="true" id="audio"
className="audio"
controls="controls">
Your browser does not support the audio element.
</audio>
<span id="microphone"></span>
</div>
</div>
<!-- PAYLOAD DON'T TOUCH/FOR TEXT->JSON -->
<div id="payload-column" class="fixed-column content-column">
<div id="payload-request" class="payload"></div>
<div id="payload-response" class="payload"></div>
</div>
<!-- SCRIPTS -->
<script src="https://code.jquery.com/jquery-3.1.1.min.js"
integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8="
crossorigin="anonymous"></script>
<script src="js/jquery-ajax-native.js"></script>
<script type="text/javascript" src="js/Microphone.js"></script>
<script type="text/javascript" src="js/SpeechToText.js"></script>
<script type="text/javascript" src="js/speechsearch.js"></script>
<script src="js/hideChat.js"></script>
<script src="js/speech-input.js"></script>
<script src="js/common.js"></script>
<script src="js/api.js"></script>
<script src="js/payload.js"></script>
<script src="js/global.js"></script>
<script src="js/analytics.js"></script>
</body>
</html>
I'm not sure what to do but as the ajax is called and jquery is used in client side js, help is appreciated.

Categories