714 lines
24 KiB
HTML
714 lines
24 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Document</title>
|
|
|
|
<style>
|
|
table {
|
|
/* width: 100%; */
|
|
border-collapse: collapse;
|
|
font-family: sans-serif;
|
|
font-size: 0.95rem;
|
|
background-color: #fff;
|
|
border: 1px solid #ccc;
|
|
|
|
max-height: 400px;
|
|
overflow-y: scroll;
|
|
|
|
margin: 10px;
|
|
}
|
|
|
|
th, td {
|
|
padding: 0.25em 1em;
|
|
border: 1px solid #ddd;
|
|
text-align: left;
|
|
}
|
|
|
|
th {
|
|
background-color: #f4f4f4;
|
|
font-weight: bold;
|
|
}
|
|
|
|
tr:nth-child(even) {
|
|
background-color: #f9f9f9;
|
|
}
|
|
|
|
tr:hover {
|
|
background-color: #f1f1f1;
|
|
}
|
|
|
|
div.container table {
|
|
max-height: 100vh;
|
|
overflow-y: scroll;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
body {
|
|
font-family: Arial, sans-serif;
|
|
margin: 20px;
|
|
padding: 0;
|
|
background-color: #f4f4f4;
|
|
}
|
|
|
|
.container {
|
|
flex: 1;
|
|
min-width: 400px;
|
|
margin: 10px;
|
|
padding: 20px;
|
|
background-color: #fff;
|
|
border-radius: 8px;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
h2 {
|
|
text-align: center;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
label {
|
|
display: block;
|
|
margin: 8px 0 4px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
select, input[type="number"], input[type="text"] {
|
|
width: 100%;
|
|
padding: 8px;
|
|
margin-bottom: 16px;
|
|
border-radius: 4px;
|
|
border: 1px solid #ccc;
|
|
}
|
|
|
|
.row {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.row .input-group {
|
|
width: 32%;
|
|
}
|
|
|
|
.row .input-group input {
|
|
width: 100%;
|
|
}
|
|
|
|
.button-container {
|
|
text-align: center;
|
|
}
|
|
|
|
.button-container button {
|
|
padding: 10px 20px;
|
|
background-color: #4CAF50;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.button-container button:hover {
|
|
background-color: #45a049;
|
|
}
|
|
|
|
.word {
|
|
border-radius: 4px;
|
|
padding: 3px 8px;
|
|
display: inline;
|
|
border: 1px solid gray;
|
|
}
|
|
|
|
.word > input[type="checkbox"] {
|
|
display: none;
|
|
}
|
|
|
|
.word:hover > input[type="checkbox"] {
|
|
display: inline;
|
|
}
|
|
|
|
.word-selected {
|
|
background-color:#4c6faf;
|
|
}
|
|
|
|
.word:hover {
|
|
border: 1px solid black;
|
|
background-color:#0c59e7;
|
|
color: white;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.word-timer::after {
|
|
content: "s";
|
|
}
|
|
|
|
.word-done::before {
|
|
content: "❌ ";
|
|
}
|
|
|
|
.word-correct::before {
|
|
content: "✅ ";
|
|
}
|
|
|
|
.word:hover .word-correct::before {
|
|
content: "";
|
|
}
|
|
|
|
.word:hover .word-done::before {
|
|
content: "";
|
|
}
|
|
|
|
|
|
.current-word {
|
|
font-weight: bold;
|
|
}
|
|
|
|
.current-word::before {
|
|
content: "▶ ";
|
|
}
|
|
|
|
#word-list-interactive > div {
|
|
margin: 20px 10px;
|
|
}
|
|
|
|
|
|
.flex {
|
|
display: flex;
|
|
justify-content: flex-start;
|
|
flex-wrap: wrap;
|
|
gap: 20px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1 style="text-align: center;">VR Charades</h1>
|
|
|
|
<div class="flex">
|
|
<!-- <div class="container">
|
|
<h2>Facial Expressions</h2>
|
|
<div style="max-height: 80vh; overflow-y: scroll;">
|
|
<table id="facialExpressions">
|
|
<tr>
|
|
<th>Index</th>
|
|
<th>Property</th>
|
|
<th>Value Slider</th>
|
|
<th>Value Number</th>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="container">
|
|
<h2>Modify Element</h2>
|
|
|
|
<label for="element-select">Select Element</label>
|
|
<select id="element-select">
|
|
</select>
|
|
|
|
<div class="row">
|
|
<div class="input-group">
|
|
<label for="translate-x">Translation X</label>
|
|
<input type="number" id="translate-x" placeholder="X">
|
|
</div>
|
|
<div class="input-group">
|
|
<label for="translate-y">Translation Y</label>
|
|
<input type="number" id="translate-y" placeholder="Y">
|
|
</div>
|
|
<div class="input-group">
|
|
<label for="translate-z">Translation Z</label>
|
|
<input type="number" id="translate-z" placeholder="Z">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="input-group">
|
|
<label for="rotate-x">Rotation X</label>
|
|
<input type="number" id="rotate-x" placeholder="X">
|
|
</div>
|
|
<div class="input-group">
|
|
<label for="rotate-y">Rotation Y</label>
|
|
<input type="number" id="rotate-y" placeholder="Y">
|
|
</div>
|
|
<div class="input-group">
|
|
<label for="rotate-z">Rotation Z</label>
|
|
<input type="number" id="rotate-z" placeholder="Z">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="button-container">
|
|
<button type="button">Modify</button>
|
|
</div>
|
|
</div> -->
|
|
|
|
<div class="container">
|
|
<h2>Charades</h2>
|
|
|
|
<label for="group-id">Group ID</label>
|
|
<input type="text" id="group-id" placeholder="g1">
|
|
|
|
<label for="condition-mode">Condition/Mode</label>
|
|
<select id="condition-mode">
|
|
<option value="">-- Select Condition --</option>
|
|
<option value="MODE:1;1;1;0">Dynamic Face</option>
|
|
<option value="MODE:0;0;0;1">Dynamic Hand</option>
|
|
<option value="MODE:1;1;1;1">Dynamic Hand+Face</option>
|
|
<option value="MODE:1;0;0;0">Static Face</option>
|
|
<option value="MODE:0;0;0;1">Static Hand</option>
|
|
<option value="MODE:1;0;0;1">Static Hand+Face</option>
|
|
</select>
|
|
|
|
<label for="ip-player1">IP Player 1</label>
|
|
<input type="text" id="ip-player1" placeholder="10.42.0.38">
|
|
|
|
<label for="ip-player2">IP Player 2</label>
|
|
<input type="text" id="ip-player2" placeholder="10.42.0.100">
|
|
|
|
<fieldset style="display: block; margin: 20px 0px;">
|
|
<legend>Current Player For Acting</legend>
|
|
|
|
<div style="margin: 0.4em;">
|
|
<input id="chosen-player-1" name="chosen-player" type="radio" value="player1" checked />
|
|
<label style="display: inline; font-weight: normal;" for="chosen-player-1">Player 1</label>
|
|
</div>
|
|
|
|
<div style="margin: 0.4em;">
|
|
<input id="chosen-player-2" name="chosen-player" type="radio" value="player2" />
|
|
<label style="display: inline; font-weight: normal;" for="chosen-player-2">Player 2</label>
|
|
</div>
|
|
</fieldset>
|
|
|
|
<div class="row" style="margin-bottom: 20px;">
|
|
<div class="input-group">
|
|
<label for="time-s">Time per Word (s)</label>
|
|
<input type="number" id="time-s" placeholder="30" value="30">
|
|
</div>
|
|
<div class="input-group">
|
|
<label for="total-duration">Total Duration (min)</label>
|
|
<input type="number" id="total-duration" placeholder="5" value="0" step="0.5">
|
|
<small style="color: #666; font-size: 0.85em;">0 = no limit</small>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<label for="last-word-status">Last Word Status</label>
|
|
<select id="last-word-status">
|
|
<option value="-1">None</option>
|
|
<option value="0">False</option>
|
|
<option value="1">Correct</option>
|
|
</select>
|
|
|
|
<div class="row">
|
|
<div class="input-group">
|
|
<label for="word">Word</label>
|
|
<input type="text" id="word" placeholder="Word...">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="button-container">
|
|
<button type="button" id="button-word">Send</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="container">
|
|
<textarea id="word-list" name="word-list" rows="40" cols="50" placeholder="charade words"></textarea>
|
|
<br>
|
|
|
|
<div class="button-container">
|
|
<button type="button" id="button-shuffle-words">Shuffle</button>
|
|
<button type="button" id="button-create-word-items">Modify</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="container" style="overflow-x: scroll; max-height: 80vh;">
|
|
<div id="word-list-interactive">
|
|
<p>
|
|
Press <b>Modify</b> to generate and run the word list.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="button-container">
|
|
<button type="button" id="button-save-to-file">Save as CSV</button>
|
|
<button type="button" id="button-stop">Stop</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="total-timer" style="display: none; width: calc(100% - 40px); margin: 20px; padding: 30px; background-color: #4CAF50; color: white; border-radius: 12px; text-align: center; font-weight: bold; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);">
|
|
<div style="margin-bottom: 10px; font-size: 1.5em; opacity: 0.95;">Total Time Remaining</div>
|
|
<div id="total-timer-display" style="font-size: 4em; font-family: 'Courier New', monospace; letter-spacing: 0.1em;">00:00</div>
|
|
</div>
|
|
|
|
<script>
|
|
let runningWordIndex = -1;
|
|
let runningWordList = [];
|
|
let experimentStartTime = null;
|
|
let totalDurationMs = 0;
|
|
|
|
function createWordItems() {
|
|
const wordList = document.getElementById("word-list");
|
|
const text = wordList.value;
|
|
const wordListInteractive = document.getElementById("word-list-interactive");
|
|
wordListInteractive.innerHTML = "";
|
|
runningWordList = [];
|
|
runningWordIndex = -1;
|
|
|
|
let index = 0;
|
|
text.trim().split('\n').forEach((word) => {
|
|
index += 1;
|
|
|
|
const div = document.createElement("div");
|
|
div.classList.add("word");
|
|
div.style.display = "flex";
|
|
div.setAttribute("word", word.trim());
|
|
|
|
const div2 = document.createElement("div");
|
|
div2.style.width = "100%";
|
|
div2.style.display = "flex";
|
|
div2.style.justifyContent = "space-between";
|
|
|
|
const p = document.createElement("p");
|
|
const span = document.createElement("span");
|
|
span.innerText = `Word ${index}: `;
|
|
span.style.color = "gray";
|
|
const wordText = document.createTextNode(word.trim());
|
|
p.appendChild(span);
|
|
p.appendChild(wordText);
|
|
|
|
|
|
const input = document.createElement("input");
|
|
input.setAttribute("type", "checkbox");
|
|
input.addEventListener("input", () => {
|
|
if (input.checked) {
|
|
p.classList.add("word-correct");
|
|
} else {
|
|
p.classList.remove("word-correct");
|
|
}
|
|
});
|
|
|
|
const timerP = document.createElement("p");
|
|
timerP.classList.add("word-timer");
|
|
|
|
const timeSeconds = parseFloat(document.getElementById("time-s").value);
|
|
timerP.innerText = timeSeconds.toFixed(2);
|
|
timerP.setAttribute("remainingTime", timeSeconds.toFixed(2))
|
|
|
|
div2.appendChild(p);
|
|
div2.appendChild(timerP);
|
|
|
|
div.appendChild(input);
|
|
div.appendChild(div2);
|
|
wordListInteractive.appendChild(div);
|
|
|
|
runningWordList.push(div);
|
|
});
|
|
}
|
|
|
|
let frameId = undefined;
|
|
let last = undefined;
|
|
let newWord;
|
|
let lastWordStatus = -1;
|
|
function step(timestamp) {
|
|
if (last === undefined) {
|
|
last = timestamp;
|
|
experimentStartTime = timestamp;
|
|
runningWordIndex = 0;
|
|
newWord = true;
|
|
lastWordStatus = -1; // First word has no previous word
|
|
}
|
|
const elapsed = timestamp - last;
|
|
last = timestamp;
|
|
|
|
// Check if total duration has been exceeded
|
|
if (totalDurationMs > 0) {
|
|
const totalElapsed = timestamp - experimentStartTime;
|
|
const remainingMs = totalDurationMs - totalElapsed;
|
|
|
|
// Update timer display
|
|
const remainingMinutes = Math.floor(remainingMs / 60000);
|
|
const remainingSeconds = Math.floor((remainingMs % 60000) / 1000);
|
|
const timerDisplay = document.getElementById("total-timer-display");
|
|
timerDisplay.textContent = `${remainingMinutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`;
|
|
|
|
// Change color when time is running out
|
|
const timerContainer = document.getElementById("total-timer");
|
|
if (remainingMs < 60000) { // Less than 1 minute
|
|
timerContainer.style.backgroundColor = "#f44336"; // Red
|
|
} else if (remainingMs < 120000) { // Less than 2 minutes
|
|
timerContainer.style.backgroundColor = "#ff9800"; // Orange
|
|
} else {
|
|
timerContainer.style.backgroundColor = "#4CAF50"; // Green
|
|
}
|
|
|
|
if (totalElapsed >= totalDurationMs) {
|
|
console.log("Total duration exceeded, stopping experiment");
|
|
cancelAnimationFrame(frameId);
|
|
frameId = undefined;
|
|
document.getElementById("total-timer").style.display = "none";
|
|
alert("Experiment completed: Total duration reached");
|
|
return;
|
|
}
|
|
}
|
|
|
|
const runningWord = runningWordList[runningWordIndex];
|
|
const p = runningWord.querySelector("p");
|
|
const timer = runningWord.querySelector(".word-timer");
|
|
let remainingTime = parseFloat(timer.getAttribute("remainingTime")) - (elapsed / 1000);
|
|
if (newWord) {
|
|
newWord = false;
|
|
let word = runningWord.getAttribute("word");
|
|
sendNewWord(word, lastWordStatus, remainingTime);
|
|
}
|
|
|
|
if (p.classList.contains("word-correct") || remainingTime < 0) {
|
|
if (remainingTime < 0) {
|
|
remainingTime = 0;
|
|
}
|
|
|
|
p.classList.add("word-done");
|
|
runningWordIndex += 1;
|
|
lastWordStatus = p.classList.contains("word-correct") ? 1 : 0;
|
|
newWord = true;
|
|
}
|
|
timer.innerText = remainingTime.toFixed(2);
|
|
timer.setAttribute("remainingTime", remainingTime);
|
|
|
|
if (runningWordIndex < runningWordList.length) {
|
|
frameId = requestAnimationFrame(step);
|
|
} else {
|
|
frameId = undefined;
|
|
}
|
|
}
|
|
|
|
document.getElementById("button-stop").addEventListener("click", () => {
|
|
// Stop the word list animation
|
|
if (frameId) {
|
|
cancelAnimationFrame(frameId);
|
|
frameId = undefined;
|
|
}
|
|
|
|
// Hide timer
|
|
document.getElementById("total-timer").style.display = "none";
|
|
|
|
console.log("Experiment stopped");
|
|
});
|
|
|
|
document.getElementById("button-save-to-file").addEventListener("click", () => {
|
|
// Check if experiment is still running
|
|
if (frameId !== undefined) {
|
|
alert("Please stop the experiment before saving results.");
|
|
return;
|
|
}
|
|
|
|
// Check if there's data to save
|
|
if (runningWordList.length === 0) {
|
|
alert("No data to save. Please run the experiment first.");
|
|
return;
|
|
}
|
|
|
|
// Get group ID for filename
|
|
const groupId = document.getElementById("group-id").value || "group";
|
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5);
|
|
|
|
let data = "word;correct;time left\n";
|
|
|
|
// Only export words that were actually used (up to current index or all if finished)
|
|
const maxIndex = runningWordIndex >= runningWordList.length ? runningWordList.length : runningWordIndex;
|
|
|
|
for (let i = 0; i < maxIndex; ++i) {
|
|
const p = runningWordList[i].querySelector("p");
|
|
const timer = runningWordList[i].querySelector(".word-timer");
|
|
|
|
const word = runningWordList[i].getAttribute("word");
|
|
const isCorrect = p.classList.contains("word-correct");
|
|
const timeLeft = parseFloat(timer.getAttribute("remainingTime"));
|
|
|
|
data += `${word};${isCorrect};${timeLeft}\n`;
|
|
}
|
|
|
|
// Create a download with proper filename
|
|
const blob = new Blob([data], { type: 'text/csv;charset=utf-8;' });
|
|
const link = document.createElement("a");
|
|
const url = URL.createObjectURL(blob);
|
|
link.setAttribute("href", url);
|
|
link.setAttribute("download", `${groupId}_results_${timestamp}.csv`);
|
|
link.style.visibility = 'hidden';
|
|
document.body.appendChild(link);
|
|
link.click();
|
|
document.body.removeChild(link);
|
|
|
|
console.log(`Saved ${maxIndex} out of ${runningWordList.length} words to CSV`);
|
|
});
|
|
|
|
document.getElementById("button-shuffle-words").addEventListener("click", async () => {
|
|
const wordList = document.getElementById("word-list");
|
|
const text = wordList.value.trim();
|
|
|
|
if (!text) {
|
|
alert("Please enter words to shuffle first.");
|
|
return;
|
|
}
|
|
|
|
// Don't allow shuffle if word list is already running
|
|
if (frameId !== undefined) {
|
|
alert("Cannot shuffle while word list is running. Please stop first.");
|
|
return;
|
|
}
|
|
|
|
const words = text.split('\n').map(word => word.trim()).filter(word => word);
|
|
|
|
try {
|
|
const response = await fetch("/shuffle", {
|
|
method: "POST",
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({ words: words }),
|
|
});
|
|
|
|
const result = await response.json();
|
|
if (result.status === "ok") {
|
|
wordList.value = result.shuffled_words.join('\n');
|
|
}
|
|
} catch (error) {
|
|
console.error("Error shuffling words:", error);
|
|
alert("Failed to shuffle words. Please try again.");
|
|
}
|
|
});
|
|
|
|
document.getElementById("button-create-word-items").addEventListener("click", () => {
|
|
// Validate inputs
|
|
const groupId = document.getElementById("group-id").value;
|
|
const conditionMode = document.getElementById("condition-mode").value;
|
|
|
|
if (!groupId || !groupId.trim()) {
|
|
alert("Please enter a Group ID before starting.");
|
|
return;
|
|
}
|
|
|
|
if (!conditionMode) {
|
|
alert("Please select a Condition/Mode before starting.");
|
|
return;
|
|
}
|
|
|
|
// Get total duration in milliseconds
|
|
const totalDurationMin = parseFloat(document.getElementById("total-duration").value) || 0;
|
|
totalDurationMs = totalDurationMin * 60 * 1000;
|
|
|
|
// Show/hide timer based on whether duration is set
|
|
const timerContainer = document.getElementById("total-timer");
|
|
if (totalDurationMs > 0) {
|
|
timerContainer.style.display = "block";
|
|
timerContainer.style.backgroundColor = "#4CAF50";
|
|
const minutes = Math.floor(totalDurationMs / 60000);
|
|
const seconds = Math.floor((totalDurationMs % 60000) / 1000);
|
|
document.getElementById("total-timer-display").textContent = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
|
|
} else {
|
|
timerContainer.style.display = "none";
|
|
}
|
|
|
|
createWordItems();
|
|
if (frameId) {
|
|
cancelAnimationFrame(frameId);
|
|
}
|
|
|
|
last = undefined;
|
|
newWord = undefined;
|
|
experimentStartTime = null;
|
|
frameId = requestAnimationFrame(step);
|
|
|
|
console.log(`Experiment started: Group=${groupId}, Mode=${conditionMode}, Duration=${totalDurationMin}min`);
|
|
});
|
|
|
|
document.getElementById('button-word').addEventListener('click', () => {
|
|
const chosenPlayer1 = document.getElementById("chosen-player-1").checked;
|
|
const chosenPlayer2 = document.getElementById("chosen-player-2").checked;
|
|
const ipPlayer1 = document.getElementById("ip-player1").value;
|
|
const ipPlayer2 = document.getElementById("ip-player2").value;
|
|
|
|
const target = chosenPlayer1 ? ipPlayer1 : ipPlayer2;
|
|
const targetOther = chosenPlayer1 ? ipPlayer2 : ipPlayer1;
|
|
const lastWordStatus = document.getElementById("last-word-status").value;
|
|
const timeSeconds = document.getElementById("time-s").value;
|
|
const word = document.getElementById("word").value;
|
|
|
|
const data = {
|
|
target: target,
|
|
lastWordStatus: parseInt(lastWordStatus),
|
|
timeSeconds: parseFloat(timeSeconds),
|
|
word: word,
|
|
};
|
|
|
|
fetch("/word", {
|
|
method: "POST",
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify(data),
|
|
}).then(res => {
|
|
// console.log("Request complete! response:", res);
|
|
});
|
|
|
|
const data2 = {
|
|
target: targetOther,
|
|
lastWordStatus: parseInt(lastWordStatus),
|
|
timeSeconds: 0.0, // parseFloat(timeSeconds),
|
|
word: "", // word,
|
|
};
|
|
|
|
fetch("/word", {
|
|
method: "POST",
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify(data),
|
|
}).then(res => {
|
|
// console.log("Request complete! response:", res);
|
|
});
|
|
});
|
|
|
|
function sendNewWord(word, lastWordStatus, timeSeconds) {
|
|
const chosenPlayer1 = document.getElementById("chosen-player-1").checked;
|
|
const chosenPlayer2 = document.getElementById("chosen-player-2").checked;
|
|
const ipPlayer1 = document.getElementById("ip-player1").value;
|
|
const ipPlayer2 = document.getElementById("ip-player2").value;
|
|
|
|
const target = chosenPlayer1 ? ipPlayer1 : ipPlayer2;
|
|
const targetOther = chosenPlayer1 ? ipPlayer2 : ipPlayer1;
|
|
|
|
console.log("target:", target);
|
|
console.log("targetOther:", targetOther);
|
|
|
|
const data = {
|
|
target: target,
|
|
lastWordStatus: lastWordStatus,
|
|
timeSeconds,
|
|
word: word,
|
|
};
|
|
|
|
fetch("/word", {
|
|
method: "POST",
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify(data),
|
|
}).then(res => {
|
|
// console.log("Request complete! response:", res);
|
|
});
|
|
|
|
const dataOther = {
|
|
target: targetOther,
|
|
lastWordStatus: lastWordStatus,
|
|
timeSeconds: 0.0,
|
|
word: "", // word,
|
|
};
|
|
|
|
fetch("/word", {
|
|
method: "POST",
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify(dataOther),
|
|
}).then(res => {
|
|
// console.log("Request complete! response:", res);
|
|
});
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|