face-api and tensorflow.js not working in browser - javascript

I'm trying to run this example in the browser
https://justadudewhohacks.github.io/face-api.js/docs/index.html#getting-started-browser
Specifically this code here
<!DOCTYPE html>
<html>
<head>
<script src="assets/face-api.js"></script>
<script src="assets/commons.js"></script>
<script src="assets/faceDetectionControls.js"></script>
<link rel="stylesheet" href="assets/styles.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/css/materialize.css">
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/js/materialize.min.js"></script>
</head>
<body>
<div id="navbar"></div>
<div class="center-content page-container">
<div class="progress" id="loader">
<div class="indeterminate"></div>
</div>
<div style="position: relative" class="margin">
<video onloadedmetadata="onPlay(this)" id="inputVideo" autoplay muted playsinline></video>
<canvas id="overlay" />
</div>
<div class="row side-by-side">
<!-- face_detector_selection_control -->
<div id="face_detector_selection_control" class="row input-field" style="margin-right: 20px;">
<select id="selectFaceDetector">
<option value="ssd_mobilenetv1">SSD Mobilenet V1</option>
<option value="tiny_face_detector">Tiny Face Detector</option>
</select>
<label>Select Face Detector</label>
</div>
<!-- face_detector_selection_control -->
<!-- check boxes -->
<div class="row" style="width: 220px;">
<input type="checkbox" id="hideBoundingBoxesCheckbox" onchange="onChangeHideBoundingBoxes(event)" />
<label for="hideBoundingBoxesCheckbox">Hide Bounding Boxes</label>
</div>
<!-- check boxes -->
<!-- fps_meter -->
<div id="fps_meter" class="row side-by-side">
<div>
<label for="time">Time:</label>
<input disabled value="-" id="time" type="text" class="bold">
<label for="fps">Estimated Fps:</label>
<input disabled value="-" id="fps" type="text" class="bold">
</div>
</div>
<!-- fps_meter -->
</div>
<!-- ssd_mobilenetv1_controls -->
<span id="ssd_mobilenetv1_controls">
<div class="row side-by-side">
<div class="row">
<label for="minConfidence">Min Confidence:</label>
<input disabled value="0.5" id="minConfidence" type="text" class="bold">
</div>
<button
class="waves-effect waves-light btn"
onclick="onDecreaseMinConfidence()"
>
<i class="material-icons left">-</i>
</button>
<button
class="waves-effect waves-light btn"
onclick="onIncreaseMinConfidence()"
>
<i class="material-icons left">+</i>
</button>
</div>
</span>
<!-- ssd_mobilenetv1_controls -->
<!-- tiny_face_detector_controls -->
<span id="tiny_face_detector_controls">
<div class="row side-by-side">
<div class="row input-field" style="margin-right: 20px;">
<select id="inputSize">
<option value="" disabled selected>Input Size:</option>
<option value="128">128 x 128</option>
<option value="160">160 x 160</option>
<option value="224">224 x 224</option>
<option value="320">320 x 320</option>
<option value="416">416 x 416</option>
<option value="512">512 x 512</option>
<option value="608">608 x 608</option>
</select>
<label>Input Size</label>
</div>
<div class="row">
<label for="scoreThreshold">Score Threshold:</label>
<input disabled value="0.5" id="scoreThreshold" type="text" class="bold">
</div>
<button
class="waves-effect waves-light btn"
onclick="onDecreaseScoreThreshold()"
>
<i class="material-icons left">-</i>
</button>
<button
class="waves-effect waves-light btn"
onclick="onIncreaseScoreThreshold()"
>
<i class="material-icons left">+</i>
</button>
</div>
</span>
<!-- tiny_face_detector_controls -->
</body>
<script>
let forwardTimes = []
let withBoxes = true
function onChangeHideBoundingBoxes(e) {
withBoxes = !$(e.target).prop('checked')
}
function updateTimeStats(timeInMs) {
forwardTimes = [timeInMs].concat(forwardTimes).slice(0, 30)
const avgTimeInMs = forwardTimes.reduce((total, t) => total + t) / forwardTimes.length
$('#time').val(`${Math.round(avgTimeInMs)} ms`)
$('#fps').val(`${faceapi.utils.round(1000 / avgTimeInMs)}`)
}
async function onPlay() {
const videoEl = $('#inputVideo').get(0)
if(videoEl.paused || videoEl.ended || !isFaceDetectionModelLoaded())
return setTimeout(() => onPlay())
const options = getFaceDetectorOptions()
const ts = Date.now()
const result = await faceapi.detectSingleFace(videoEl, options).withFaceExpressions()
updateTimeStats(Date.now() - ts)
if (result) {
const canvas = $('#overlay').get(0)
const dims = faceapi.matchDimensions(canvas, videoEl, true)
const resizedResult = faceapi.resizeResults(result, dims)
const minConfidence = 0.05
if (withBoxes) {
faceapi.draw.drawDetections(canvas, resizedResult)
}
faceapi.draw.drawFaceExpressions(canvas, resizedResult, minConfidence)
}
setTimeout(() => onPlay())
}
async function run() {
// load face detection and face expression recognition models
await changeFaceDetector(TINY_FACE_DETECTOR)
await faceapi.loadFaceExpressionModel('/')
changeInputSize(224)
// try to access users webcam and stream the images
// to the video element
const stream = await navigator.mediaDevices.getUserMedia({ video: {} })
const videoEl = $('#inputVideo').get(0)
videoEl.srcObject = stream
}
function updateResults() {}
$(document).ready(function() {
renderNavBar('#navbar', 'webcam_face_expression_recognition')
initFaceDetectionControls()
run()
})
</script>
</body>
</html>
Unfortunately this is not working ( I have loaded the associated libraries into assets, i.e., https://github.com/justadudewhohacks/face-api.js/tree/master/dist and moving the other files from this example here
https://github.com/justadudewhohacks/face-api.js/tree/master/examples/examples-browser
What am i doing wrong? I am loading this on a page on my site here
https://moodmap.app/webcamFaceExpressionRecognition.html in case you want to see what's happening in the browser.
Based on changes below,
Here is the node server where I am setting where things are held - is it possible to just change this instead? As it is coming up with a separate issue with the shard needed in the model as well now when making the changes suggested below.
Thanks!
const config = require('../../config');
const express = require('express');
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io')(server, { wsEngine: 'ws' });
const mysql = require('mysql');
const expressSession = require('express-session');
const ExpressMysqlSessionStore = require('express-mysql-session')(expressSession);
const sharedsession = require('express-socket.io-session');
const path = require('path');
const utils = require('./utils');
// remove from header "X-Powered-By: Express"
app.disable('x-powered-by');
server.listen(config.serverParams.port, config.serverParams.address, () => {
console.log(`Server running at http://${server.address().address}:${server.address().port}`);
});
/* DATABASE */
global.db = mysql.createConnection(config.db);
db.connect();
/* DATABASE */
/* SESSION */
const sessionStore = new ExpressMysqlSessionStore(config.sessionStore, db);
const session = expressSession({
...config.session,
store: sessionStore,
});
app.use(session);
/* SESSION */
app.use(express.static(config.frontendDir));
app.use(express.static(path.join(__dirname, './src/assets')))
app.use(express.static(path.join(__dirname, './src/assets/weights')))
app.use((req,res,next)=>{
//can reaplce * with website we want to allow access
res.header('Access-Control-Allow-Origin', '*');
next();
});
app.get([
'/signup',
'/stats',
'/pay',
], (req, res) => res.sendFile(path.join(`${config.frontendDir}${req.path}.html`)));
io.use(sharedsession(session, {
autoSave: true
}));
io.on('connection', socket => {
socket.use((packet, next) => {
if (packet[0]) {
console.log('METHOD:', packet[0]);
const sessionData = socket.handshake.session.user;
const noSessionNeed = [ 'login', 'signup', 'checkAuth' ].includes(packet[0]);
let error;
if ( ! sessionData && ! noSessionNeed) error = { code: -1, message: 'You need to login in extension!' };
if (error) return next(new Error(JSON.stringify(error)));
else next();
}
});
const auth = require('./auth')(socket);
socket.on('checkAuth', auth.checkAuth);
socket.on('login', auth.login);
socket.on('signup', auth.signup);
socket.on('logout', auth.logout);
const users = require('./users')(socket);
socket.on('users.get', users.get);
const sentiment = require('./sentiment')(socket);
socket.on('sentiment.get', sentiment.get);
socket.on('sentiment.set', sentiment.set);
socket.on('disconnect', () => {
});
});
Reason being still getting an error for some reason as below,?
fetchOrThrow.ts:11 Uncaught (in promise) Error: failed to fetch: (404) , from url: https://moodmap.app/assets/tiny_face_detector_model-weights_manifest.json
at fetchOrThrow.ts:11
at step (drawContour.ts:28)
at Object.next (drawContour.ts:28)
at fulfilled (drawContour.ts:28)
(anonymous) # fetchOrThrow.ts:11
step # drawContour.ts:28
(anonymous) # drawContour.ts:28
fulfilled # drawContour.ts:28
async function (async)
run # webcamFaceExpressionRecognition.html:142
(anonymous) # webcamFaceExpressionRecognition.html:158
j # jquery-2.1.1.min.js:2
fireWith # jquery-2.1.1.min.js:2
ready # jquery-2.1.1.min.js:2
I # jquery-2.1.1.min.js:2
Thanks in advance!

So, the error you were talking about in the question is:
Uncaught (in promise) Error: failed to fetch: (404) , from URL:
https://moodmap.app/tiny_face_detector_model-weights_manifest.json
So, this error implies that the file tiny_face_detector_model-weights_manifest.json could not be found. And this is happening for all other manifest.json files as well, as I see in your website.
You mentioned in the question that all your associated libraries are into assets folder. So, your tiny_face_detector_model-weights_manifest.json file and other manifest.json files also in assets folder and I am giving you the solution according to that but if you change location of files to another folder, just replace assets with that whatever folder where your file is in.
face-api.js Line:1976
Here, in line 1976 you see defaultModelName. This tells about the file to load.
1. So if your app has only the issue with loading tiny_face_detector_model-weights_manifest.json [ For this case, It is showing 404 error for all manifest.json so jump below] then
face-api.js Line: 5627
Go to line 5627 in face-api.js and change
"tiny_face_detector_model" with
"./assets/tiny_face_detector_model"
2. If all your manifest files are showing 404 error while loading, which true for this case because all your files are in assets folder.
So, in that case go to line number 1976 of face-api.js,
just replace:
var defaultManifestFilename=defaultModelName+"-weights_manifest.json";
with:
var defaultManifestFilename="./assets/"+defaultModelName+"-weights_manifest.json";
That means just concatenate the folder name or path where the manifest file exists to the mentioned variable. And this will fix the path issue for all manifest.json files in your application.
NOTE: If there is a problem finding the code with line number I mentioned then use search.

Related

I am trying to create a bug/issue tracker. It works in the web browser but I am trying to deploy with node server and the app does not work anymore

My app works in live server but when i add nodejs, it doesn't work correctly. The Modal window does not pop up anymore and I do not know why. I have understood that I cant use localstorage or window properties because it is not on the browser anymore, but I cant figure out what to do instead of using those. Is there an npm package I can install to use localstorage and other browser side properties? Just looking for someone to point out my mistake and point me in the right direction. Any help is greatly appreciated. Thanks in advance!
home.html
<!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 href="https://cdn.jsdelivr.net/npm/bootstrap#5.3.0-alpha1/dist/css/bootstrap.min.css"
rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD"
crossorigin="anonymous">
<script src="https://kit.fontawesome.com/4582c8b826.js" crossorigin="anonymous"></script>
<link rel="stylesheet" href="/css/styles.css">
<title>Issue Tracker</title>
</head>
<body>
<div class="contaienr">
<h1>Issue Tracker</h1>
<div class="jumbotron">
<h3>Add New Issue:</h3>
<form id="issueinputform">
<div class="form-group">
<label for="issueDescription">Description</label>
<input type="text" class="form-control" id="issueDescription" placeholder="Describe the issue ...">
</div>
<div class="form-group">
<label for="issueSeverity">Severity</label>
<select class="form-control" id="issueSeverity">
<option value="Low">Low</option>
<option value="Medium">Medium</option>
<option value="High">High</option>
</select>
</div>
<div class="form-group">
<label for="issueAssignedTo">Assigned To</label>
<input type="text" class="form-control" id="issueAssignedTo" placeholder="Enter responsible ...">
</div>
<button id="add-issue" onclick="submitIssue()" class="btn btn-primary">Add</button>
</form>
</div>
<div class="col-lg-12">
<div id="issuesList">
</div>
</div>
</div>
<!-- Modal -->
<div class="modal fade" id="emptyField" tabindex="-1" role="dialog" aria-labelledby="emptyFieldLabel"
aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="emptyFieldLabel">Invalid Input!</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
Please provide the desciption of the issue and also the person name who you want to assign the issue.
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<script src="app.js"></script>
<script src="https://code.jquery.com/jquery-3.6.3.min.js" integrity="sha256-pvPw+upLPUjgMXY0G+8O0xUf+/Im1MZjXxxgOcBQBXU=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.3.0-alpha1/dist/js/bootstrap.min.js" integrity="sha384-mQ93GR66B00ZXjt0YO5KlohRA5SY2XofN4zfuZxLkoj1gXtW8ANNCe9d5Y3eG5eD" crossorigin="anonymous"></script>
</body>
</html>
app.js
function submitIssue(e) {
const getInputValue = id => document.getElementById(id).value;
const description = getInputValue('issueDescription');
const severity = getInputValue('issueSeverity');
const assignedTo = getInputValue('issueAssignedTo');
const id = Math.floor(Math.random() * 100000000) + '';
const status = 'Open';
if ((description.length == 0) || (assignedTo.length == 0)) {
alert("Please fill all fields with required data.");
document.getElementById('add-issue').setAttribute("data-toggle", "modal");
document.getElementById('add-issue').setAttribute("data-target", "#emptyField")
}
else {
document.getElementById('add-issue').removeAttribute("data-toggle", "modal");
document.getElementById('add-issue').removeAttribute("data-target", "#emptyField")
const issue = { id, description, severity, assignedTo, status };
let issues = [];
if (localStorage.getItem('issues')) {
issues = JSON.parse(localStorage.getItem('issues'));
}
issues.push(issue);
localStorage.setItem('issues', JSON.stringify(issues));
fetchIssues();
}
}
const closeIssue = id => {
const issues = JSON.parse(localStorage.getItem('issues'));
const currentIssue = issues.find(issue => issue.id == id);
currentIssue.status = 'Closed';
currentIssue.description = `<strike>${currentIssue.description}</strike>`
localStorage.setItem('issues', JSON.stringify(issues));
fetchIssues();
}
const deleteIssue = id => {
const issues = JSON.parse(localStorage.getItem('issues'));
const remainingIssues = issues.filter(issue => ((issue.id) != id))
localStorage.removeItem('issues');
localStorage.setItem('issues', JSON.stringify(remainingIssues));
fetchIssues();
}
const fetchIssues = () => {
const issues = JSON.parse(localStorage.getItem('issues'));
const issuesList = document.getElementById('issuesList');
issuesList.innerHTML = '';
for (let i = 0; i < issues.length; i++) {
const { id, description, severity, assignedTo, status } = issues[i];
issuesList.innerHTML += `<div class="well">
<h6>Issue ID: ${id} </h6>
<p><span class="label label-info"> ${status} </span></p>
<h3> ${description} </h3>
<p><i class="fa-solid fa-bolt"></i> ${severity}</p>
<p><i class="fa-solid fa-user"></i> ${assignedTo}</p>
<button onclick="closeIssue(${id})" class="btn btn-warning">Close</button>
<button onclick="deleteIssue(${id})" class="btn btn-danger">Delete</button>
</div>`;
}
}
fetchIssues();
main.js(server)
const bodyParser = require('body-parser');
const express = require('express');
const mongoose = require('mongoose');
const ejs = require('ejs')
const app = express();
const path = require('path');
const morgan = require('morgan')
const bcrypt = require('bcrypt');
const saltRounds = 10;
app.use(express.static('public'));
app.set('view engine', 'ejs');
app.use(bodyParser.urlencoded({extended: true}));
app.use(morgan('tiny'))
mongoose.connect("mongodb://localhost:27017/userDB", {useNewUrlParser: true});
const userSchema = new mongoose.Schema ({
email: String,
password: String
});
const User = new mongoose.model("User", userSchema)
app.get('/', function(req,res){
// res.sendFile(path.join(__dirname, '/index.html'));
res.redirect('/login')
});
app.get('/login', function (req,res){
// res.sendFile(path.join(__dirname, '/login/login.html'));
res.render('login')
})
app.get('/register', function(req,res){
res.render("register")
});
app.post('/register', function(req,res){
bcrypt.hash(req.body.password, saltRounds, function(err,hash) {
const newUser = new User({
email: req.body.username,
password: hash
});
newUser.save(function(err){
if(err){
console.log(err)
} else{
res.sendFile(path.join(__dirname+'/home.html'));
}
});
});
});
app.get('/home', function(req,res){
res.sendFile(path.join(__dirname+'/home.html'));
// appSubmit.submitIssue()
})
// app.get('/home?', function(req,res){
// // res.sendFile(path.join(__dirname+'/home.html'));
// appSubmit.submitIssue();
// })
app.post('/login', function(req,res){
const username = req.body.username;
const password = req.body.password;
User.findOne({email: username}, function(err,foundUser){
if(err){
console.log(err)
} else{
if(foundUser){
bcrypt.compare(password, foundUser.password, function(err, result) {
// res.sendFile(path.join(__dirname+'/home.html'));
res.redirect('/home')
});
}
}
})
})
app.listen(5000,function(){
console.log("server runnin on 5000")
})

how can I pass the data that is passed in a form to a json file

I'm working with nodejs, I have an crud app for storing data, I want to pass the data to a main json file.
my files are like this:
index.js
'use strict'
const fs = require("fs");
const express = require("express");
const app = express();
const path = require("path");
app.use(express.static('public'))
app.set("view engine", "ejs");
app.set("views", path.join(__dirname, "views"));
app.get("/", ( req, res) => {
res.render("home.ejs");
})
app.listen((3000), ()=> {
console.log("listening on port 3000..");
})
home.ejs
<!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">
<!-- page title -->
<title>Sellers - Added</title>
<!-- bootstrap css -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.2.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi" crossorigin="anonymous">
<!-- local css -->
<link rel="stylesheet" href="/css/app.css">
</head>
<body>
<!-- main wrapper start -->
<div class="container mt-5">
<!-- title start -->
<div class="title text-center">
<h1 class="display-5">
<i class="fas fa-user-alt text-success"></i> <strong>Sellers List</strong>
</h1>
</div>
<!-- title end -->
<!-- content start -->
<div class="main row">
<!-- left form start -->
<div class="col-sm-12 col-md-4">
<!-- page form -->
<form id="seller-form" autocomplete="off">
<!-- seller name start -->
<div class="form-group">
<label for="name">Seller Name:</label>
<input type="text" id="name" class="form-control">
</div>
<!-- seller name end -->
<!-- seller domain start -->
<div class="form-group">
<label for="domain">Domain:</label>
<input type="text" id="domain" class="form-control">
</div>
<!-- seller domain end -->
<!-- seller type start -->
<div class="form-group">
<label for="type">Seller Type:</label>
<input type="text" id="type" class="form-control">
</div>
<!-- seller type end -->
<!-- submit button start -->
<input type="submit" value="Submit" class="btn btn-success btn-block add-btn">
<!-- submit button end -->
</form>
</div>
<!-- left form end -->
<!-- list start -->
<div class="col-sm-12 col-md-8">
<table class="table table-striped mt-5">
<thead>
<tr>
<th>Name</th>
<th>Domain</th>
<th>Type</th>
<th>ID</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody id="seller-list"></tbody>
</table>
</div>
<!-- list end -->
</div>
<!-- content end -->
</div>
<!-- main wrapper end -->
</body>
<!-- JavaScript Bundle with Popper -->
<script src="/js/script.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.2.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-OERcA2EqjJCMA+/3y+gxIOqMEjwtxJY7qPCqsdltbNJuaOe923+mo//f6V8Qbsw3" crossorigin="anonymous"></script>
</html>
script.js
let selectedRow = null;
class Seller {
constructor(name, domain, type, id) {
this.name = name;
this.domain = domain;
this.type = type;
this.id = id;
}
}
// UI Class: Handle UI Tasks
class UI {
// add seller to list
static addSellerToList(seller) {
const list = document.querySelector('#seller-list');
const row = document.createElement('tr');
row.innerHTML = `
<td>${seller.name}</td>
<td>${seller.domain}</td>
<td>${seller.type}</td>
<td>${seller.id}</td>
<td>Edit</td>
<td>X</td>
`;
list.appendChild(row);
}
// edit seller to list
static editSellerToList(seller) {
selectedRow.children[0].textContent = seller.name;
selectedRow.children[1].textContent = seller.domain;
selectedRow.children[2].textContent = seller.type;
}
// delete seller
static deleteSeller(el) {
if(el.classList.contains('delete')) {
el.parentElement.parentElement.remove();
UI.showAlert('Seller Deleted', 'danger');
} else null
}
// edit seller
static editSeller(el) {
if(el.classList.contains('edit')) {
selectedRow = el.parentElement.parentElement;
document.querySelector('#name').value = selectedRow.children[0].textContent;
document.querySelector('#domain').value = selectedRow.children[1].textContent;
document.querySelector('#type').value = selectedRow.children[2].textContent;
} else null
}
static showAlert(message, className) {
const div = document.createElement('div');
div.className = `alert alert-${className}`;
div.appendChild(document.createTextNode(message));
const container = document.querySelector('.container');
const main = document.querySelector('.main');
container.insertBefore(div, main);
setTimeout(() => document.querySelector('.alert').remove(), 2000);
}
static clearFields() {
document.querySelector('#name').value = '';
document.querySelector('#domain').value = '';
document.querySelector('#type').value = '';
}
}
// Store Class: Handles Storage
// class Store {
// static getSellers() {
// let sellers;
// if(localStorage.getItem('sellers') === null) {
// sellers = [];
// } else {
// sellers = JSON.parse(localStorage.getItem('sellers'));
// }
// return sellers;
// }
// static addSeller(seller) {
// const sellers = Store.getSellers();
// sellers.push(seller);
// localStorage.setItem('sellers', JSON.stringify(sellers));
// }
// static removeSeller(type) {
// const sellers = Store.getSellers();
// sellers.forEach((seller, index) => {
// if(seller.type === type) {
// sellers.splice(index, 1);
// }
// });
// localStorage.setItem('sellers', JSON.stringify(sellers));
// }
// }
// Event: Display Sellers
document.addEventListener('DOMContentLoaded',UI.displaySellers);
// Event: Add a Seller
document.querySelector('#seller-form').addEventListener('submit', (e) => {
// Prevent actual submit
e.preventDefault();
// Get form values
const name = document.querySelector('#name').value;
const domain = document.querySelector('#domain').value;
const type = document.querySelector('#type').value;
const id = GenerateUniqueID();
// Validate
if(name === '' || domain === '' || type === '') {
UI.showAlert('Please fill in all fields', 'danger');
} else {
// Instatiate seller
const seller = new Seller(name, domain, type, id);
if (selectedRow == null) {
// Add Seller to UI
UI.addSellerToList(seller);
selectedRow = null;
UI.showAlert('Seller Added', 'success');
} else {
UI.editSellerToList(seller);
selectedRow = null;
UI.showAlert('Seller Info Edited', 'info');
}
// Add seller to store
// Store.addSeller(seller);
// Show success message
// Clear fields
UI.clearFields();
}
});
// Event: Remove a Seller
document.querySelector('#seller-list').addEventListener('click', (e) => {
// Remove seller from UI
UI.deleteSeller(e.target);
UI.editSeller(e.target);
// Remove seller from store
// Store.removeSeller(e.target.parentElement.previousElementSibling.textContent);
// Show success message
// UI.showAlert('Seller Removed', 'success');
});
function GenerateUniqueID() {
return ('0000'+(Math.random() * (100000 - 101) + 101)|0).toString().slice(-5);
}
again, I just need the data to save to a new json file.
any help is very appriciated, thanks :)

Keeping Modal Form Open After Failed Submission Attempt

I am working on a Nodejs project and currently working on making a modal form stay open after a user fails to meet form requirements, such as filling out all the form fields. I've done some reading online and am having trouble understanding how I would do this. Currently, I just have the form render a new page when there is a mistake completing the form. The repo to this project is: https://github.com/halsheik/RecipeWarehouse.git. Below, I have also pasted the relevant chunks of my code for this problem. I would appreciate any assistance.
// Modules required to run the application
const express = require('express');
const multer = require('multer');
const crypto = require('crypto');
const path = require('path');
const { ensureAuthenticated } = require('../config/auth');
// Creates 'mini app'
const router = express.Router();
// Models
const Recipe = require('../models/Recipe'); // Recipe Model
// Set up storage engine
const storage = multer.diskStorage({
destination: function(req, file, callback){
callback(null, 'public/uploads');
},
filename: function(req, file, callback){
crypto.pseudoRandomBytes(16, function(err, raw) {
if (err) return callback(err);
callback(null, raw.toString('hex') + path.extname(file.originalname));
});
}
});
const upload = multer({
storage: storage
});
// My Recipes
router.get('/myRecipes', ensureAuthenticated, function(req, res){
Recipe.find({}, function(err, recipes){
if(err){
console.log(err);
} else {
res.render('./home/myRecipes', {
recipes: recipes,
recipeImageFileName: recipes.recipeImageFileName,
recipeDescription: recipes.recipeDescription,
ingredients: recipes.ingredients,
directions: recipes.directions
});
}
});
});
// Create Recipe
router.post('/createRecipe', upload.single('recipeImage'), ensureAuthenticated, function(req, res){
const { recipeName, recipeDescription, ingredients, directions } = req.body;
let errors = [];
// Checks that all fields are not empty
if(!recipeName || !recipeDescription || !ingredients || !directions){
errors.push({ msg: 'Please fill in all fields.' });
}
// Checks that an image is uploaded
if(!req.file){
errors.push({ msg: 'Please add an image of your recipe' });
}
// Checks for any errors and prevents recipe creation if any
if(errors.length > 0){
console.log(errors);
Recipe.find({}, function(err, recipes){
if(err){
console.log(err);
} else {
res.render('./home/myRecipes', {
errors: errors,
recipes: recipes,
recipeImageFileName: recipes.recipeImageFileName,
recipeDescription: recipes.recipeDescription,
ingredients: recipes.ingredients,
directions: recipes.directions
});
}
});
} else {
// Create a new 'Recipe' using our model
const newRecipe = new Recipe({
recipeName: recipeName,
author: req.user._id,
recipeImageFileName: req.file.filename,
recipeDescription: recipeDescription,
ingredients: ingredients,
directions: directions,
});
console.log(newRecipe);
// Saves recipe to mongoDB database
newRecipe.save().then(function(){
res.redirect('/recipes/myRecipes');
}).catch(function(err){
console.log(err);
});
}
});
module.exports = router;
<%- include('../_partial/_header'); -%>
<div id="newRecipeContainer">
<div class="overlay"></div>
<div id="closeButtonContainer">
<div id="closeButton">+</div>
</div>
<form action="/recipes/createRecipe" method="POST" enctype="multipart/form-data">
<label id="formSubHeading">Create Your Homemade Recipe</label>
<div id="recipeNameContainer">
<label id="recipeNameLabel">Title</label>
<input id="recipeNameInput" type="text" name="recipeName">
</div>
<div id="recipeImage">
<label id="recipeImageLabel">Add An Image of Your Meal</label>
<input id="recipeImageInput" type="file" accept="image/*" name="recipeImage" onchange="validateImageFile(this);"/>
<label id="recipeImageInputLabel" for="recipeImageInput">Choose A File</label>
</div>
<div id="recipeDescription">
<label id="recipeDescriptionLabel">Description</label>
<textarea id="recipeDescriptionInput" name="recipeDescription" cols="30" rows="10" maxlength="2000"></textarea>
</div>
<div class="ingredientsContainer">
<label id="ingredientsLabel">Ingredients</label>
<button id="addIngredientButton" type="button" #click="addIngredientForm">Add Another Ingredient</button>
<div class="allIngredients" v-for="(ingredient, ingredientIndex) in ingredients">
<label class="ingredientLabel">{{ ingredientIndex + 1 }}.)</label>
<input class="ingredientInput" type="text" name="ingredients" v-model="ingredient.ingredient">
<button class="deleteIngredientButton" type="button" v-if="ingredientIndex > 0" #click="deleteIngredientForm(ingredientIndex)">X</button>
</div>
</div>
<div class="directionsContainer">
<label id="directionsLabel">Directions</label>
<button id="addDirectionButton" type="button" #click="addDirectionForm">Add Another Direction</button>
<div class="allDirections" v-for="(direction, directionIndex) in directions">
<label class="directionLabel">{{ directionIndex + 1 }}.)</label>
<input class="directionInput"type="text" name="directions" v-model="direction.direction">
<button class="deleteDirectionButton" type="button" v-if="directionIndex > 0" #click="deleteDirectionForm(directionIndex)">X</button>
</div>
</div>
<div id="createRecipeButtonContainer">
<button id="createRecipeButton" type="submit">Create Recipe</button>
</div>
</form>
</div>
<div id="recipesContainer">
<div id="myRecipesContainer">
<label id="myRecipesLabel">My Recipes</label>
<a id="newRecipeButton">+ Create New Recipe</a>
</div>
<div id="allRecipes">
<% recipes.forEach(function(recipe){ %>
<% if(recipe.author == user._id){ %>
<div class="secondaryContainer">
<div class="recipeContainerIndv">
<img src="/uploads/<%= recipe.recipeImageFileName %>"/>
<a class="recipeTitle"> <%= recipe.recipeName %> </a>
<!-- <% recipe.directions.forEach(function(direction){ %>
<a><%= direction %></a>
<% }); %> -->
</div>
</div>
<% } %>
<% }); %>
</div>
</div>
<script src="/controls/newRecipeControl.js"></script>
<script src="/controls/imageValidator.js"></script>
<%- include('../_partial/_footer'); -%>
const directionsControl = new Vue({
el: '.directionsContainer',
data: {
directions: [
{
direction: ''
}
]
},
methods: {
addDirectionForm: function(){
this.directions.push({
direction: ''
})
},
deleteDirectionForm: function(directionIndex){
if(directionIndex)
this.directions.splice(directionIndex, 1)
}
}
})
const ingredientsControl = new Vue({
el: '.ingredientsContainer',
data: {
ingredients: [
{
ingredient: ''
}
]
},
methods: {
addIngredientForm: function(){
this.ingredients.push({
ingredient: ''
})
},
deleteIngredientForm: function(ingredientIndex){
if(ingredientIndex)
this.ingredients.splice(ingredientIndex, 1)
}
}
})
const toggleNewRecipeForm = function(){
const newRecipeButton = document.querySelector('#newRecipeButton');
const newRecipeContainer = document.querySelector('#newRecipeContainer');
const closeButton = document.querySelector('#closeButton');
const overlay = document.querySelector('.overlay');
// Open dropDownMenu
newRecipeButton.addEventListener('click', function(){
newRecipeContainer.style.display = 'block';
overlay.classList.toggle('addOverlay');
});
// Close dropDownMenu
closeButton.addEventListener('click', function(){
newRecipeContainer.style.display = 'none';
overlay.classList.toggle('addOverlay');
});
}
toggleNewRecipeForm();
To keep the hole page from refreshing you would have to look into client-side frameworks I would recommend learning AngularJS it would make you able to refresh the form without refreshing the page.
I would also recommend some client-side framework, in my case i use bootstrap and jquery in this way:
<!-- Button trigger modal -->
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal">
Launch demo modal
</button>
<!-- Modal -->
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
...
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function() {
$('button[value=save]').click(function(e) {
e.preventDefault();
// ....
// Code to check validity
// ....
// Hides modal
$('#exampleModal').modal('hide');
});
});
</script>

How to load extensions for the Forge Viewer (without a viewerApp)

I'm trying to developp a Forge Autodesk Viewer for a webapp, this tutorial. I have an issue while trying to load extensions, indeed they never load on the viewer.
I've already developped the viewer of the tutorial, and the extensions worked correctly.
The main difference between my viewer and the tutorial's viewer is the use of a viewerApp in the tutorial while I had to use directly a GUIViewer3D (For the aggregation of several models).
I've already tried to load the viewer and the extensions in a different order, but it didn't change worked either. I assumed the code of the extension is correct, since it works in the tutorial, but I'm not sure about how I linked it to my viewer.
The code to load the viewer :
Autodesk.Viewing.Initializer(options, function onInitialized() {
// Initialisation du Viewer
var viewerDiv = document.getElementById('MyViewerDiv');
var config = {
extensions: ['DockingPanelExtension']
};
viewer = new Autodesk.Viewing.Private.GuiViewer3D(viewerDiv, config);
viewer.initialize();
});
The code of the index
<head>
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=no" />
<meta charset="utf-8">
<!-- The Viewer CSS -->
<link rel="stylesheet" href="https://developer.api.autodesk.com/modelderivative/v2/viewers/6.*/style.min.css"
type="text/css">
<!-- Developer CSS -->
<link rel="stylesheet" href="/static/style.css" type="text/css">
<!-- Common packages: jQuery, Bootstrap, jsTree -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jstree/3.3.7/jstree.min.js"></script>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/jstree/3.3.7/themes/default/style.min.css" />
</head>
<body>
<!-- Fixed navbar by Bootstrap: https://getbootstrap.com/examples/navbar-fixed-top/ -->
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container-fluid">
<ul class="nav navbar-nav left">
<li>
<a href="http://developer.autodesk.com" target="_blank">
<img alt="IM-Pact" src="static/img/IMPact.png"
height="20">
</a>
</li>
<li>
<button type="button" class="btn btn-default navbar-btn" onClick="callNew()">Add next model</button>
</li>
</ul>
</div>
</nav>
<!-- End of navbar -->
<div class="container-fluid fill">
<div class="row fill">
<div class="col-sm-4 fill">
<div class="panel panel-default fill">
<div class="panel-heading" data-toggle="tooltip">
Buckets & Objects
<span id="refreshBuckets" class="glyphicon glyphicon-refresh" style="cursor: pointer"></span>
<button class="btn btn-xs btn-info" style="float: right" id="showFormCreateBucket"
data-toggle="modal" data-target="#createBucketModal">
<span class="glyphicon glyphicon-folder-close"></span> New bucket
</button>
</div>
<div id="appBuckets">
tree here
</div>
</div>
</div>
<div class="col-sm-8 fill">
<div id="MyViewerDiv"></div>
</div>
</div>
</div>
<form id="uploadFile" method='post' enctype="multipart/form-data">
<input id="hiddenUploadField" type="file" name="theFile" style="visibility:hidden" />
</form>
<!-- Modal Create Bucket -->
<div class="modal fade" id="createBucketModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Cancel">
<span aria-hidden="true">×</span>
</button>
<h4 class="modal-title" id="myModalLabel">Create new bucket</h4>
</div>
<div class="modal-body">
<input type="text" id="newBucketKey" class="form-control"> For demonstration purposes, objects
(files) are
NOT automatically translated. After you upload, right click on
the object and select "Translate". Bucket keys must be of the form [-_.a-z0-9]{3,128}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="createNewBucket">Go ahead, create the
bucket</button>
</div>
</div>
</div>
</div>
<!-- <button id="MyNextButton" onClick="callNext()">Next!</button> -->
<!-- The Viewer JS -->
<script src="https://developer.api.autodesk.com/modelderivative/v2/viewers/viewer3D.min.js?v=v6.6"></script>
<!-- Developer JS -->
<script src="static/js/docLoad.js"></script>
<script src="static/js/modelLoad.js"></script>
<script src="static/js/extensions/dockingpannelextension.js"></script>
<script src="static/js/viewer.js"></script>
<script src="static/js/tree.js"></script>
</body>
The code of the extension
// *******************************************
// Model Summary Extension
// *******************************************
var propsToList = [];
function addToList(item) {
if (propsToList.includes(item)) {
var index = propsToList.indexOf(item);
propsToList.splice(index, 1);
} else {
propsToList.push(item)
}
console.log(propsToList)
}
function ModelSummaryExtension(viewer, options) {
Autodesk.Viewing.Extension.call(this, viewer, options);
this.panel = null; // create the panel variable
}
ModelSummaryExtension.prototype = Object.create(Autodesk.Viewing.Extension.prototype);
ModelSummaryExtension.prototype.constructor = ModelSummaryExtension;
ModelSummaryExtension.prototype.load = function () {
if (this.viewer.toolbar) {
// Toolbar is already available, create the UI
this.createUI();
} else {
// Toolbar hasn't been created yet, wait until we get notification of its creation
this.onToolbarCreatedBinded = this.onToolbarCreated.bind(this);
this.viewer.addEventListener(Autodesk.Viewing.TOOLBAR_CREATED_EVENT, this.onToolbarCreatedBinded);
}
return true;
};
ModelSummaryExtension.prototype.onToolbarCreated = function () {
this.viewer.removeEventListener(Autodesk.Viewing.TOOLBAR_CREATED_EVENT, this.onToolbarCreatedBinded);
this.onToolbarCreatedBinded = null;
this.createUI();
};
ModelSummaryExtension.prototype.createUI = function () {
var _this = this;
// prepare to execute the button action
var modelSummaryToolbarButton = new Autodesk.Viewing.UI.Button('runModelSummaryCode');
modelSummaryToolbarButton.onClick = function (e) {
// check if the panel is created or not
if (_this.panel == null) {
_this.panel = new ModelSummaryPanel(_this.viewer, _this.viewer.container, 'modelSummaryPanel', 'Model Summary');
}
// show/hide docking panel
_this.panel.setVisible(!_this.panel.isVisible());
// if panel is NOT visible, exit the function
if (!_this.panel.isVisible()) return;
// ok, it's visible, let's get the summary!
// first, the Viewer contains all elements on the model, including
// categories (e.g. families or part definition), so we need to enumerate
// the leaf nodes, meaning actual instances of the model. The following
// getAllLeafComponents function is defined at the bottom
_this.getAllLeafComponents(function (dbIds) {
// now for leaf components, let's get some properties
// and count occurrences of each value.
// get only the properties we need for the leaf dbIds
_this.viewer.model.getBulkProperties(dbIds, propsToList, function (dbIdsProps) {
// iterate through the elements we found
dbIdsProps.forEach(function (item) {
// and iterate through each property
item.properties.forEach(function (itemProp) {
// now use the propsToList to store the count as a subarray
if (propsToList[itemProp.displayName] === undefined)
propsToList[itemProp.displayName] = {};
// now start counting: if first time finding it, set as 1, else +1
if (propsToList[itemProp.displayName][itemProp.displayValue] === undefined)
propsToList[itemProp.displayName][itemProp.displayValue] = 1;
else
propsToList[itemProp.displayName][itemProp.displayValue] += 1;
});
});
// now ready to show!
// the Viewer PropertyPanel has the .addProperty that receives the name, value
// and category, that simple! So just iterate through the list and add them
propsToList.forEach(function (propName) {
if (propsToList[propName] === undefined) return;
Object.keys(propsToList[propName]).forEach(function (propValue) {
_this.panel.addProperty(
/*name*/
propValue,
/*value*/
propsToList[propName][propValue],
/*category*/
propName);
});
});
})
})
};
// modelSummaryToolbarButton CSS class should be defined on your .css file
// you may include icons, below is a sample class:
modelSummaryToolbarButton.addClass('modelSummaryToolbarButton');
modelSummaryToolbarButton.setToolTip('Model Summary');
// SubToolbar
this.subToolbar = (this.viewer.toolbar.getControl("MyAppToolbar") ?
this.viewer.toolbar.getControl("MyAppToolbar") :
new Autodesk.Viewing.UI.ControlGroup('MyAppToolbar'));
this.subToolbar.addControl(modelSummaryToolbarButton);
this.viewer.toolbar.addControl(this.subToolbar);
};
ModelSummaryExtension.prototype.unload = function () {
this.viewer.toolbar.removeControl(this.subToolbar);
return true;
};
ModelSummaryExtension.prototype.getAllLeafComponents = function (callback) {
var cbCount = 0; // count pending callbacks
var components = []; // store the results
var tree; // the instance tree
function getLeafComponentsRec(parent) {
cbCount++;
if (tree.getChildCount(parent) != 0) {
tree.enumNodeChildren(parent, function (children) {
getLeafComponentsRec(children);
}, false);
} else {
components.push(parent);
}
if (--cbCount == 0) callback(components);
}
this.viewer.getObjectTree(function (objectTree) {
tree = objectTree;
var allLeafComponents = getLeafComponentsRec(tree.getRootId());
});
};
// *******************************************
// Model Summary Panel
// *******************************************
function ModelSummaryPanel(viewer, container, id, title, options) {
this.viewer = viewer;
Autodesk.Viewing.UI.PropertyPanel.call(this, container, id, title, options);
}
ModelSummaryPanel.prototype = Object.create(Autodesk.Viewing.UI.PropertyPanel.prototype);
ModelSummaryPanel.prototype.constructor = ModelSummaryPanel;
Autodesk.Viewing.theExtensionManager.registerExtension('ModelSummaryExtension', ModelSummaryExtension);
Thanks in advance !
In the extension JavaScript file, you're registering the extension under the name ModelSummaryExtension, but in the viewer initialization code you're passing the config object with extensions: ['DockingPanelExtension']. That's likely why the extension isn't loaded. Try initializing the GuiViewer3D class with the following config instead:
let config = {
extensions: ['ModelSummaryExtension']
};
EDIT (after the extension naming has been fixed):
When initializing the GuiViewer3D, call its start() method instead of initialize(). It will internally call initialize() (for initializing internal structures, event handlers, etc.), setUp(); (for configuring the viewer based on your config object), and finally it will call loadModel() if there's a URN or a filepath argument passed to the function.

Electron child window doesn't want to call its own js script

I'm very much not a javascript person, but I've been making an Electron GUI, and I'm getting confused by why I can't seem to make a child browser window call its own js.
I have a main.js that does a bunch of things, setting up a tray menu, one option of which will spawn a browser window like this:
window = new BrowserWindow({
width: 360,
height: 320,
backgroundColor: "#D6D8DC",
show: false
})
window.setMenuBarVisibility(false);
window.loadURL(require('url').format({
pathname: path.join(__dirname, 'index.html'),
protocol: 'file:',
slashes: true
}))
And that window loads fine, and includes this reference to its own window.js, which is the way I've seen it done in Electron examples:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Radiam Agent</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://code.jquery.com/jquery-3.2.1.js"></script>
</head>
<body>
<div id="container" class="container-fluid">
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 ">
<h3>Username</h3>
<input name="username" id="username" type="text" size="40"/>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 ">
<h3>Password</h3>
<input name="password" id="password" type="password" size="40"/>
</div>
</div>
<br/><br/>
<div class="button">
<button id="login">Get Radiam Login Token</button>
</div>
</div>
<script src="window.js" charset="utf-8"></script>
</body>
</html>
But the window.js code, which looks like this (the entire file), doesn't seem to fire ever:
const notifier = require("node-notifier");
const zerorpc = require("zerorpc");
let client = new zerorpc.Client();
client.connect("tcp://127.0.0.1:4242");
function login(event) {
var username = document.getElementById("username").value;
var password = document.getElementById("password").value;
client.invoke("login", username, password, function(error, res, more) {
if (res){
notifier.notify({"title" : "Radiam", "message" : res});
}
});
}
document.addEventListener('DOMContentLoaded', function() {
document.getElementById("login").addEventListener("click", login(event));
})
Am I missing something? Again, not a js person (I'm building a GUI to a Python script, which is otherwise working great), but it seems like it should be obvious...
Thanks!
This worked. I think it was the wrong type of listener, plus I shouldn't have been trying to pass event:
const notifier = require("node-notifier");
const zerorpc = require("zerorpc");
let client = new zerorpc.Client();
client.connect("tcp://127.0.0.1:4242");
function login() {
var username = document.getElementById("username").value;
var password = document.getElementById("password").value;
client.invoke("login", username, password, function(error, res, more) {
if (res){
notifier.notify({"title" : "Radiam", "message" : res});
}
});
}
document.getElementById("login").addEventListener("click", login);

Categories