This commit is contained in:
Leo 2024-01-25 17:46:28 +08:00
commit 8b944268a2
69 changed files with 10901 additions and 3222 deletions

View File

@ -1,9 +1,10 @@
const { run } = require("./modules/IoT-sensor");
const { IoTdataGenerator } = require("./modules/IoT-sensor");
const client = require("./modules/mqtt");
async function publishData() {
try {
const data = await run();
let iothub = new IoTdataGenerator();
let data = await iothub.generateData();
console.log(data);
client.publish("iot-data", JSON.stringify(data));
} catch (err) {

View File

@ -18,19 +18,19 @@ class IoTdataGenerator {
async generateData() {
try {
const { loc, sen } = await this.getLocationAndSensorId();
const dataAray = []; // create a new array for each call
for (let i = 0; i < sen.length; i++) {
//console.log(sen[i].id);
//console.log(loc[i].id);
//console.log("you should appear 6 times only")
dataAray.push(firstDataRow(sen[i].id, loc[i].id));
}
}
return dataAray;
} catch (err) {
console.error(err);
}
return dataAray;
}
}
//helper function to generate random data
function firstDataRow(sensorId, locationId) {
@ -57,15 +57,7 @@ function firstDataRow(sensorId, locationId) {
2) loop through each sensor id and location id and generate random data and pass to mqtt
*/
async function run() {
let iotData = new IoTdataGenerator();
const result = await iotData.generateData();
console.log(result);
return result;
}
module.exports = { run };
module.exports = { IoTdataGenerator };

42
Sean/modules/otpUtils.js Normal file
View File

@ -0,0 +1,42 @@
const nodemailer = require("nodemailer");
const otpGenerator = require('otp-generator');
const path = require('path')
require('dotenv').config({ path: path.resolve(__dirname, '../.env') })
const generateOTP = () => {
const otp = otpGenerator.generate(6, { upperCase: false, specialChars: false });
const expirationTime = Date.now() + 5 * 60 * 1000; // 5 minutes expiration
return { otp, expirationTime };
};
const sendOTPByEmail = async (email, otp) => {
try {
const transporter = nodemailer.createTransport({
service: 'gmail',
host: 'smtp.gmail.com',
port: 587, // use the appropriate port for your SMTP server
secure: false, // true for 465, false for other ports
auth: {
user: process.env.euser, // replace with your email
pass: process.env.epass // replace with your email password
}
});
const mailOptions = {
from: process.env.euser,
to: email,
subject: 'Login OTP',
text: `Your OTP for login is: ${otp}`
};
await transporter.sendMail(mailOptions);
console.log('OTP sent successfully to', email);
} catch (error) {
console.error('Error sending OTP:', error);
throw error;
}
};
module.exports = {
generateOTP,
sendOTPByEmail
};

View File

@ -0,0 +1,9 @@
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // limit each IP to 5 requests per windowMs
message: 'Too many login attempts from this IP, please try again later.',
});
module.exports = limiter;

View File

@ -0,0 +1,77 @@
const { body } = require('express-validator');
const locationValidation = [
body('name').trim().isLength({ min: 1 }).withMessage('Name must not be empty').escape(),
body('added_by').trim().isLength({ min: 1 }).withMessage('Added by must not be empty').escape(),
body('description').trim().escape(),
];
const locationValidationUpdate = [
body('id').trim().escape(),
body('name').trim().isLength({ min: 1 }).withMessage('Name must not be empty').escape(),
body('added_by').trim().isLength({ min: 1 }).withMessage('Added by must not be empty').escape(),
body('description').trim().escape(),
];
const locationdeleteValidation = [
body('id').trim().escape()
];
const sensorValidation = [
body('sensorname').trim().isLength({ min: 1 }).withMessage('Sensor Name must not be empty').escape(),
body('added_by').trim().isLength({ min: 1 }).withMessage('Added by must not be empty').escape(),
body('macAddress').custom(value => {
const macAddressRegex = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/;
if (!macAddressRegex.test(value)) {
throw new Error('Invalid MAC address format');
}
return true;
}).withMessage('Invalid MAC address format').escape(),
body('description').trim().escape(),
body('location').trim().escape()
];
const sensorupdateValidation = [
body('id').trim().escape(),
body('sensorname').trim().isLength({ min: 1 }).withMessage('Sensor Name must not be empty').escape(),
body('added_by').trim().isLength({ min: 1 }).withMessage('Added by must not be empty').escape(),
body('macAddress').custom(value => {
const macAddressRegex = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/;
if (!macAddressRegex.test(value)) {
throw new Error('Invalid MAC address format');
}
return true;
}).withMessage('Invalid MAC address format').escape(),
body('description').trim().escape(),
body('location').trim().escape()
];
const sensordeleteValidation = [
body('id').trim().escape()
];
const loginValidation = [
body('username').escape().trim().isLength({ min: 1 }).withMessage('Username must not be empty'),
body('password').escape().trim().isLength({ min: 1 }).withMessage('Password must not be empty'),
];
const otpValidation = [
body('otp').escape().trim().isLength({ min: 1 }).withMessage('OTP must not be empty'),
];
const createValidation = [
body('name').trim().isLength({ min: 1 }).withMessage('Name must not be empty').escape(),
body('username').trim().isLength({ min: 1 }).withMessage('Username must not be empty').escape(),
body('email').isEmail().withMessage('Invalid email address').normalizeEmail(),
body('password').custom((value) => {
if (!isStrongPassword(value)) { throw new Error('Password does not meet complexity requirements'); } return true;
}),
body('jobTitle').trim().isLength({ min: 1 }).withMessage('Job title must not be empty').escape(),
];
module.exports = {
locationValidation,locationValidationUpdate,locationdeleteValidation
,sensorValidation,sensorupdateValidation,sensordeleteValidation,loginValidation,otpValidation
,createValidation
};

View File

@ -1,26 +1,25 @@
const express = require("express");
const session = require("express-session");
const rateLimit = require('express-rate-limit');
const cookieParser = require('cookie-parser');
const bodyParser = require("body-parser");
const bcrypt = require("bcrypt");
const crypto = require("crypto");
const nodemailer = require("nodemailer");
const otpGenerator = require('otp-generator');
const { body, validationResult } = require('express-validator');
const validator = require('validator');
const axios = require('axios');
const {validationResult } = require('express-validator');
const {locationValidation, locationValidationUpdate, locationdeleteValidation
,sensorValidation, sensorupdateValidation, sensordeleteValidation, loginValidation
,otpValidation, createValidation} = require('./modules/validationMiddleware');
const rateLimit = require('./modules/rateLimitMiddleware');
const { generateOTP, sendOTPByEmail } = require('./modules/otpUtils');
const { format } = require('date-fns');
const helmet = require('helmet');
const { Sequelize } = require('sequelize');
const { transporter } = require("./modules/nodeMailer");
const { sequelize, User } = require("./modules/mysql");
const userLogs= require('./models/userLogs')(sequelize); // Adjust the path based on your project structure
const userLogs= require('./models/userLogs')(sequelize);
const app = express();
const nonce = crypto.randomBytes(16).toString('base64');
console.log('Nonce:', nonce);
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(cookieParser());
@ -30,17 +29,6 @@ require("dotenv").config();
app.use(bodyParser.urlencoded({ extended: true }));
app.set("view engine", "ejs");
app.use(
helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'",`'nonce-${nonce}'`],
scriptSrc: ["'self'",`'nonce-${nonce}'`,"'strict-dynamic'", 'cdn.jsdelivr.net', 'fonts.googleapis.com', 'stackpath.bootstrapcdn.com', 'code.jquery.com', 'cdnjs.cloudflare.com'],
styleSrc: ["'self'",`'nonce-${nonce}'`, 'cdn.jsdelivr.net', 'fonts.googleapis.com'],
imgSrc: ["'self'"],
fontSrc: ["'self'", 'fonts.gstatic.com'],
},
})
);
app.use(session({
secret: process.env.key,
@ -56,79 +44,33 @@ function isAuthenticated(req, res, next) {
if (req.session && req.session.authenticated) {
return next();
} else {
res.redirect("/login");
res.redirect("/index");
}
}
const generateOTP = () => {
const otp = otpGenerator.generate(6, { upperCase: false, specialChars: false });
const expirationTime = Date.now() + 5 * 60 * 1000; // 5 minutes expiration
return { otp, expirationTime };
};
const sendOTPByEmail = async (email, otp) => {
try {
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: process.env.euser, // replace with your email
pass: process.env.epass // replace with your email password
}
});
const mailOptions = {
from: process.env.euser,
to: email,
subject: 'Login OTP',
text: `Your OTP for login is: ${otp}`
};
await transporter.sendMail(mailOptions);
console.log('OTP sent successfully to', email);
} catch (error) {
console.error('Error sending OTP:', error);
throw error;
}
};
app.get('/index', (req, res) => {
res.render('index');
});
app.get("/login", (req, res) => {
res.render("login", { error: null });
});
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // limit each IP to 3 requests per windowMs
message: 'Too many login attempts from this IP, please try again later.',
});
app.use('/login', limiter);
app.use('/login', rateLimit);
app.post('/login', [
body('username').escape().trim().isLength({ min: 1 }).withMessage('Username must not be empty'),
body('password').escape().trim().isLength({ min: 1 }).withMessage('Password must not be empty'),
],
async (req, res) => {
try {
const errors = validationResult(req);
app.post('/login', loginValidation, async (req, res) => {
try {const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.render('login', { error: 'Invalid input. Please check your credentials.', csrfToken: req.session.csrfToken });
}
let { username, password } = req.body;
username = username.trim();
const user = await User.findOne({ where: { username } });
if (user) {
const isLoginSuccessful = await bcrypt.compare(password, user.password);
if (isLoginSuccessful) {
await userLogs.create({ username, success: true, activity: "Credentials entered correctly" });
const { otp, expirationTime } = generateOTP();
req.session.otp = otp;
@ -174,17 +116,12 @@ async (req, res) => {
// OTP verification route
app.post("/verify-otp", [
body('otp').escape().trim().isLength({ min: 1 }).withMessage('OTP must not be empty'),
],
async (req, res) => {
app.post("/verify-otp", otpValidation ,async (req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.render('otp', { error: 'Invalid OTP. Please try again.'});
}
const enteredOTP = req.body.otp;
if (!req.session) {
@ -222,17 +159,10 @@ app.post("/verify-otp", [
req.session.authenticated = true;
req.session.username = req.body.username;
req.session.sessionToken = sessionToken;
csrfTokenSession = crypto.randomBytes(32).toString('hex');
// Log anti-CSRF token
console.log(`Generated Anti-CSRF Token: ${csrfTokenSession}`);
res.cookie('sessionToken', sessionToken, { secure: true, httpOnly: true, expires: new Date(Date.now() + 24 * 60 * 60 * 1000) }); // Expires in 1 day
console.log(`Generated Session Token: ${sessionToken}`);
res.redirect("/home");
} else {
if (req.body.username) {
@ -275,17 +205,11 @@ app.post("/verify-otp", [
}
});
app.get("/home", isAuthenticated, (req, res) => {
// Render the home page with sensor data
res.render("home", {
username: req.session.username,
});
app.get("/home", isAuthenticated, async (req, res) => {
res.render("home", { username: req.session.username});
});
app.get("/inusers", isAuthenticated, async (req, res) => {
try {
// Fetch all user data from the database using Sequelize
@ -294,9 +218,8 @@ app.post("/verify-otp", [
});
const currentUsername = req.session.username;
// Render the inusers page with JSON data
res.render("inusers", { nonce: nonce, allUsers, csrfToken: csrfTokenSession, currentUsername });
res.render("inusers", {allUsers, csrfToken: csrfTokenSession, currentUsername });
} catch (error) {
console.error("Error fetching all users:", error);
res.status(500).send("Internal Server Error");
@ -332,20 +255,8 @@ function isStrongPassword(password) {
return true;
}
app.post(
'/createUser',
[
body('name').trim().isLength({ min: 1 }).withMessage('Name must not be empty').escape(),
body('username').trim().isLength({ min: 1 }).withMessage('Username must not be empty').escape(),
body('email').isEmail().withMessage('Invalid email address').normalizeEmail(),
body('password').custom((value) => {
if (!isStrongPassword(value)) { throw new Error('Password does not meet complexity requirements'); } return true;
}),
body('jobTitle').trim().isLength({ min: 1 }).withMessage('Job title must not be empty').escape(),
],
async (req, res) => {
'/createUser', createValidation, async (req, res) => {
try {
const errors = validationResult(req);
@ -477,24 +388,13 @@ app.post("/forgot-password", async (req, res) => {
const error = "Username or email not found.";
return res.render("forgot-password", { error, success: null });
}
// Generate reset token and update the user
const reset_token = crypto.randomBytes(20).toString("hex");
const reset_token_expiry = new Date(Date.now() + 3600000); // Token expires in 1 hour
// Update the user with the reset token and expiry
await User.update(
{
reset_token,
reset_token_expiry,
},
{
where: {
id: user.id, // Replace 'id' with the actual primary key field of your User model
},
}
await User.update({reset_token,reset_token_expiry,},
{where: {id: user.id},}
);
// Send email with reset link
const resetLink = `http://localhost:3000/reset-password/${reset_token}`;
const mailOptions = {
@ -502,12 +402,9 @@ app.post("/forgot-password", async (req, res) => {
subject: "Password Reset",
text: `Click on the following link to reset your password: ${resetLink}`,
};
await transporter.sendMail(mailOptions);
const success = "Password reset email sent successfully. Check your inbox.";
res.render("forgot-password", { error: null, success });
// Log the successful sending of the reset link in the database
await userLogs.create({
username: user.username,
@ -523,29 +420,22 @@ app.post("/forgot-password", async (req, res) => {
console.error("Error during password reset:", error);
const errorMessage = "An error occurred during the password reset process.";
res.render("forgot-password", { error: errorMessage, success: null });
}
});
}});
app.post("/reset-password/:token", async (req, res) => {
try {
const { token } = req.params;
const { password, confirmPassword } = req.body;
// Sanitize the inputs
const sanitizedToken = validator.escape(token);
const sanitizedPassword = validator.escape(password);
const sanitizedConfirmPassword = validator.escape(confirmPassword);
// Find user with matching reset token and not expired
const user = await User.findOne({
where: {
reset_token: sanitizedToken,
reset_token_expiry: { [Sequelize.Op.gt]: new Date() },
where: {reset_token: sanitizedToken,
reset_token_expiry: { [Sequelize.Op.gt]: new Date() },
},
});
if (!user) {
// Pass the error to the template when rendering the reset-password page
return res.render("reset-password", {
@ -553,7 +443,6 @@ app.post("/forgot-password", async (req, res) => {
resetError: "Invalid or expired reset token",
});
}
// Check if passwords match
if (sanitizedPassword !== sanitizedConfirmPassword) {
// Pass the error to the template when rendering the reset-password page
@ -562,31 +451,24 @@ app.post("/forgot-password", async (req, res) => {
resetError: "Passwords do not match",
});
}
// Check if the new password meets complexity requirements
if (!isStrongPassword(sanitizedPassword)) {
// Pass the error to the template when rendering the reset-password page
return res.render("reset-password", {
token,
resetError:
token, resetError:
"Password does not meet complexity requirements. It must be at least 10 characters long and include at least one uppercase letter, one lowercase letter, one digit, and one symbol.",
});
}
// Hash the new password
const saltRounds = 10;
const hashedPassword = await bcrypt.hash(sanitizedPassword, saltRounds);
// Update user's password and clear reset token
const updateQuery = {
password: hashedPassword,
reset_token: null,
reset_token_expiry: null,
};
const whereCondition = {
reset_token: sanitizedToken,
};
const whereCondition = {reset_token: sanitizedToken,};
await User.update(updateQuery, {
where: whereCondition,
});
@ -633,10 +515,8 @@ app.post("/reset-password", async (req, res) => {
return res.status(403).json({ error: 'CSRF token mismatch' });
}
const sessionTokencookie = req.cookies['sessionToken'];
// Verify sessionToken with the one stored in the database
const user = await User.findOne({ where: { sessionid: sessionTokencookie } });
if (!user) {
return res.status(403).json({ error: 'Invalid sessionToken' });
}
@ -644,12 +524,10 @@ app.post("/reset-password", async (req, res) => {
const sanitizedUsername = validator.escape(username);
const sanitizedPassword = validator.escape(password);
const sanitizedConfirmPassword = validator.escape(confirmPassword);
// Check if passwords match
if (sanitizedPassword !== sanitizedConfirmPassword) {
return res.status(400).json({ error: "Passwords do not match" });
}
// Check if the new password meets complexity requirements
if (!isStrongPassword(sanitizedPassword)) {
return res.status(400).json({
@ -657,31 +535,25 @@ app.post("/reset-password", async (req, res) => {
"Password does not meet complexity requirements. It must be at least 10 characters long and include at least one uppercase letter, one lowercase letter, one digit, and one symbol.",
});
}
try {
// Find the user in the database
const user = await User.findOne({ where: { username: sanitizedUsername } });
if (!user) {
return res.status(404).json({ error: "User does not exist" });
}
// Generate a random salt and hash the new password
const saltRounds = 10;
const hashedPassword = await bcrypt.hash(sanitizedPassword, saltRounds);
// Update user's password
await User.update(
{ password: hashedPassword },
{ where: { username: sanitizedUsername } }
);
// Log password reset activity
await userLogs.create({
username: creatorUsername,
activity: `Password has been reset for ${sanitizedUsername}`,
});
// Password update successful
return res.status(200).json({ success: "Password updated successfully" });
} catch (error) {
@ -693,10 +565,8 @@ app.post("/reset-password", async (req, res) => {
app.get('/searchUser', async (req, res) => {
const { username } = req.query;
// Sanitize the input
const sanitizedUsername = validator.escape(username);
try {
// Find the user in the database
const user = await User.findOne({ where: { username: sanitizedUsername } });
@ -704,11 +574,7 @@ app.get('/searchUser', async (req, res) => {
if (!user) {
// No user found with the given username
res.status(404).json({ success: false, error: 'User not found' });
} else {
// User found, return user data
res.json(user);
}
} else {res.json(user)}
} catch (error) {
console.error('Sequelize query error:', error);
res.status(500).json({ success: false, error: 'Internal Server Error' });
@ -719,7 +585,6 @@ app.get('/api/users', async (req, res) => {
try {
// Find all users in the database
const users = await User.findAll();
// Return the users in the response
res.json(users);
} catch (error) {
@ -787,9 +652,6 @@ app.delete('/api/deleteUser/:username', async (req, res) => {
res.status(500).json({ success: false, error: 'Internal Server Error', details: error.message });
}
});
app.get('/api/getLogs', async (req, res) => {
try {
@ -814,6 +676,197 @@ app.get('/api/getLogs', async (req, res) => {
}
});
app.get("/locations", isAuthenticated, async (req, res) => {
try {
// Fetch data using Axios
const response = await axios.get(process.env.API_ALLLOCATION);
const locationsData = response.data;
// Render the "locations" page with the fetched JSON data
res.render("locations", { locationsData, csrfToken: csrfTokenSession});
} catch (error) {
console.error("Error fetching locations:", error);
res.status(500).send("Internal Server Error");
}
});
app.post('/location/new', locationValidation, async (req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const sessionTokencookie = req.cookies['sessionToken'];
const user = await User.findOne({ where: { sessionid: sessionTokencookie } });
if (!user) {
return res.status(403).json({ error: 'Invalid sessionToken' });
}
const submittedCSRFToken = req.body.csrf_token;
if (!csrfTokenSession || submittedCSRFToken !== csrfTokenSession) {
return res.status(403).json({ error: 'CSRF token mismatch' });
}
const { name, added_by, description } = req.body;
const preparedData = {name, added_by, description};
// Make a POST request with the sanitized data using Axios
const axiosResponse = await axios.post(process.env.API_NEWLOCATION, preparedData);
// Send the Axios response back to the client
res.status(axiosResponse.status).json(axiosResponse.data);
} catch (error) {
console.error('Error handling new location submission:', error);
res.status(500).json({ message: 'Internal Server Error' });
}
});
app.post('/location/update', locationValidationUpdate, async (req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const sessionTokencookie = req.cookies['sessionToken'];
const user = await User.findOne({ where: { sessionid: sessionTokencookie } });
if (!user) {
return res.status(403).json({ error: 'Invalid sessionToken' });
}
const submittedCSRFToken = req.body.csrf_token;
if (!csrfTokenSession || submittedCSRFToken !== csrfTokenSession) {
return res.status(403).json({ error: 'CSRF token mismatch' });
}
const { id, name, added_by, description } = req.body;
const preparedData = {id, name, added_by, description};
// Make a POST request with the sanitized data using Axios
const axiosResponse = await axios.post(process.env.API_UPDATELOCATION, preparedData);
// Send the Axios response back to the client
res.status(axiosResponse.status).json(axiosResponse.data);
} catch (error) {
console.error('Error handling new location submission:', error);
res.status(500).json({ message: 'Internal Server Error' });
}
});
app.post('location/delete',locationdeleteValidation, async (req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const sessionTokencookie = req.cookies['sessionToken'];
const user = await User.findOne({ where: { sessionid: sessionTokencookie } });
if (!user) {
return res.status(403).json({ error: 'Invalid sessionToken' });
}
const submittedCSRFToken = req.body.csrf_token;
if (!csrfTokenSession || submittedCSRFToken !== csrfTokenSession) {
return res.status(403).json({ error: 'CSRF token mismatch' });
}
const {id} = req.body;
const preparedData = {id};
// Make a POST request with the sanitized data using Axios
const axiosResponse = await axios.post(process.env.API_DELLOCATION, preparedData);
// Send the Axios response back to the client
res.status(axiosResponse.status).json(axiosResponse.data);
} catch (error) {
console.error('Error handling new sensor submission:', error);
res.status(500).json({ message: 'Internal Server Error' });
}
});
app.get("/sensors", isAuthenticated, async (req, res) => {
try {
// Render the inusers page with JSON data
const response = await axios.get(process.env.API_ALLLOCATION);
const locationsData = response.data;
const response2 = await axios.get(process.env.API_ALLSENSOR);
const sensorData = response2.data;
res.render("sensors",{locationsData, sensorData, csrfToken: csrfTokenSession});
} catch (error) {
console.error("Error:", error);
res.status(500).send("Internal Server Error");
}
});
app.post('sensor/new',sensorValidation, async (req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const sessionTokencookie = req.cookies['sessionToken'];
const user = await User.findOne({ where: { sessionid: sessionTokencookie } });
if (!user) {
return res.status(403).json({ error: 'Invalid sessionToken' });
}
const submittedCSRFToken = req.body.csrf_token;
if (!csrfTokenSession || submittedCSRFToken !== csrfTokenSession) {
return res.status(403).json({ error: 'CSRF token mismatch' });
}
const { sensorname, added_by, macAddress, description, location} = req.body;
const preparedData = {sensorname, added_by, macAddress, description, location};
// Make a POST request with the sanitized data using Axios
const axiosResponse = await axios.post(process.env.API_NEWSENSOR, preparedData);
// Send the Axios response back to the client
res.status(axiosResponse.status).json(axiosResponse.data);
} catch (error) {
console.error('Error handling new sensor submission:', error);
res.status(500).json({ message: 'Internal Server Error' });
}
});
app.post('sensor/update',sensorupdateValidation, async (req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const sessionTokencookie = req.cookies['sessionToken'];
const user = await User.findOne({ where: { sessionid: sessionTokencookie } });
if (!user) {
return res.status(403).json({ error: 'Invalid sessionToken' });
}
const submittedCSRFToken = req.body.csrf_token;
if (!csrfTokenSession || submittedCSRFToken !== csrfTokenSession) {
return res.status(403).json({ error: 'CSRF token mismatch' });
}
const { id, sensorname, added_by, macAddress, description, location} = req.body;
const preparedData = {id, sensorname, added_by, macAddress, description, location};
// Make a POST request with the sanitized data using Axios
const axiosResponse = await axios.post(process.env.API_NEWSENSOR, preparedData);
// Send the Axios response back to the client
res.status(axiosResponse.status).json(axiosResponse.data);
} catch (error) {
console.error('Error handling new sensor submission:', error);
res.status(500).json({ message: 'Internal Server Error' });
}
});
app.post('sensor/delete',sensordeleteValidation, async (req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const sessionTokencookie = req.cookies['sessionToken'];
const user = await User.findOne({ where: { sessionid: sessionTokencookie } });
if (!user) {
return res.status(403).json({ error: 'Invalid sessionToken' });
}
const submittedCSRFToken = req.body.csrf_token;
if (!csrfTokenSession || submittedCSRFToken !== csrfTokenSession) {
return res.status(403).json({ error: 'CSRF token mismatch' });
}
const {id} = req.body;
const preparedData = {id};
// Make a POST request with the sanitized data using Axios
const axiosResponse = await axios.post(process.env.API_DELSENSOR, preparedData);
// Send the Axios response back to the client
res.status(axiosResponse.status).json(axiosResponse.data);
} catch (error) {
console.error('Error handling new sensor submission:', error);
res.status(500).json({ message: 'Internal Server Error' });
}
});
app.use(express.static("views"));
app.listen(PORT, () => {

4074
Sean/views/assets/animatecss/animate.css vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,7 @@
/*!
* Bootstrap Reboot v5.0.1 (https://getbootstrap.com/)
* Copyright 2011-2021 The Bootstrap Authors
* Copyright 2011-2021 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){h1{font-size:2.5rem}}h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){h2{font-size:2rem}}h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){h3{font-size:1.75rem}}h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){h4{font-size:1.5rem}}h5{font-size:1.25rem}h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:.875em}mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,265 @@
.navbar-dropdown {
left: 0;
padding: 0;
position: absolute;
right: 0;
top: 0;
transition: all 0.45s ease;
z-index: 1030;
background: #282828; }
.navbar-dropdown .navbar-logo {
margin-right: 0.8rem;
transition: margin 0.3s ease-in-out;
vertical-align: middle; }
.navbar-dropdown .navbar-logo img {
height: 3.125rem;
transition: all 0.3s ease-in-out; }
.navbar-dropdown .navbar-logo.mbr-iconfont {
font-size: 3.125rem;
line-height: 3.125rem; }
.navbar-dropdown .navbar-caption {
font-weight: 700;
white-space: normal;
vertical-align: -4px;
line-height: 3.125rem !important; }
.navbar-dropdown .navbar-caption, .navbar-dropdown .navbar-caption:hover {
color: inherit;
text-decoration: none; }
.navbar-dropdown .mbr-iconfont + .navbar-caption {
vertical-align: -1px; }
.navbar-dropdown.navbar-fixed-top {
position: fixed; }
.navbar-dropdown .navbar-brand span {
vertical-align: -4px; }
.navbar-dropdown.bg-color.transparent {
background: none; }
.navbar-dropdown.navbar-short .navbar-brand {
padding: 0.625rem 0; }
.navbar-dropdown.navbar-short .navbar-brand span {
vertical-align: -1px; }
.navbar-dropdown.navbar-short .navbar-caption {
line-height: 2.375rem !important;
vertical-align: -2px; }
.navbar-dropdown.navbar-short .navbar-logo {
margin-right: 0.5rem; }
.navbar-dropdown.navbar-short .navbar-logo img {
height: 2.375rem; }
.navbar-dropdown.navbar-short .navbar-logo.mbr-iconfont {
font-size: 2.375rem;
line-height: 2.375rem; }
.navbar-dropdown.navbar-short .mbr-table-cell {
height: 3.625rem; }
.navbar-dropdown .navbar-close {
left: 0.6875rem;
position: fixed;
top: 0.75rem;
z-index: 1000; }
.navbar-dropdown .hamburger-icon {
content: "";
display: inline-block;
vertical-align: middle;
width: 16px;
-webkit-box-shadow: 0 -6px 0 1px #282828,0 0 0 1px #282828,0 6px 0 1px #282828;
-moz-box-shadow: 0 -6px 0 1px #282828,0 0 0 1px #282828,0 6px 0 1px #282828;
box-shadow: 0 -6px 0 1px #282828,0 0 0 1px #282828,0 6px 0 1px #282828; }
.dropdown-menu .dropdown-toggle[data-toggle="dropdown-submenu"]::after {
border-bottom: 0.35em solid transparent;
border-left: 0.35em solid;
border-right: 0;
border-top: 0.35em solid transparent;
margin-left: 0.3rem; }
.dropdown-menu .dropdown-item:focus {
outline: 0; }
.nav-dropdown {
font-size: 0.75rem;
font-weight: 500;
height: auto !important; }
.nav-dropdown .nav-btn {
padding-left: 1rem; }
.nav-dropdown .link {
margin: .667em 1.667em;
font-weight: 500;
padding: 0;
transition: color .2s ease-in-out; }
.nav-dropdown .link.dropdown-toggle {
margin-right: 2.583em; }
.nav-dropdown .link.dropdown-toggle::after {
margin-left: .25rem;
border-top: 0.35em solid;
border-right: 0.35em solid transparent;
border-left: 0.35em solid transparent;
border-bottom: 0; }
.nav-dropdown .link.dropdown-toggle[aria-expanded="true"] {
margin: 0;
padding: 0.667em 3.263em 0.667em 1.667em; }
.nav-dropdown .link::after,
.nav-dropdown .dropdown-item::after {
color: inherit; }
.nav-dropdown .btn {
font-size: 0.75rem;
font-weight: 700;
letter-spacing: 0;
margin-bottom: 0;
padding-left: 1.25rem;
padding-right: 1.25rem; }
.nav-dropdown .dropdown-menu {
border-radius: 0;
border: 0;
left: 0;
margin: 0;
padding-bottom: 1.25rem;
padding-top: 1.25rem;
position: relative; }
.nav-dropdown .dropdown-submenu {
margin-left: 0.125rem;
top: 0; }
.nav-dropdown .dropdown-item {
font-weight: 500;
line-height: 2;
padding: 0.3846em 4.615em 0.3846em 1.5385em;
position: relative;
transition: color .2s ease-in-out, background-color .2s ease-in-out; }
.nav-dropdown .dropdown-item::after {
margin-top: -0.3077em;
position: absolute;
right: 1.1538em;
top: 50%; }
.nav-dropdown .dropdown-item:focus, .nav-dropdown .dropdown-item:hover {
background: none; }
@media (max-width: 767px) {
.nav-dropdown.navbar-toggleable-sm {
bottom: 0;
display: none;
left: 0;
overflow-x: hidden;
position: fixed;
top: 0;
transform: translateX(-100%);
-ms-transform: translateX(-100%);
-webkit-transform: translateX(-100%);
width: 18.75rem;
z-index: 999; } }
.nav-dropdown.navbar-toggleable-xl {
bottom: 0;
display: none;
left: 0;
overflow-x: hidden;
position: fixed;
top: 0;
transform: translateX(-100%);
-ms-transform: translateX(-100%);
-webkit-transform: translateX(-100%);
width: 18.75rem;
z-index: 999; }
.nav-dropdown-sm {
display: block !important;
overflow-x: hidden;
overflow: auto;
padding-top: 3.875rem; }
.nav-dropdown-sm::after {
content: "";
display: block;
height: 3rem;
width: 100%; }
.nav-dropdown-sm.collapse.in ~ .navbar-close {
display: block !important; }
.nav-dropdown-sm.collapsing, .nav-dropdown-sm.collapse.in {
transform: translateX(0);
-ms-transform: translateX(0);
-webkit-transform: translateX(0);
transition: all 0.25s ease-out;
-webkit-transition: all 0.25s ease-out;
background: #282828; }
.nav-dropdown-sm.collapsing[aria-expanded="false"] {
transform: translateX(-100%);
-ms-transform: translateX(-100%);
-webkit-transform: translateX(-100%); }
.nav-dropdown-sm .nav-item {
display: block;
margin-left: 0 !important;
padding-left: 0; }
.nav-dropdown-sm .link,
.nav-dropdown-sm .dropdown-item {
border-top: 1px dotted rgba(255, 255, 255, 0.1);
font-size: 0.8125rem;
line-height: 1.6;
margin: 0 !important;
padding: 0.875rem 2.4rem 0.875rem 1.5625rem !important;
position: relative;
white-space: normal; }
.nav-dropdown-sm .link:focus, .nav-dropdown-sm .link:hover,
.nav-dropdown-sm .dropdown-item:focus,
.nav-dropdown-sm .dropdown-item:hover {
background: rgba(0, 0, 0, 0.2) !important;
color: #c0a375; }
.nav-dropdown-sm .nav-btn {
position: relative;
padding: 1.5625rem 1.5625rem 0 1.5625rem; }
.nav-dropdown-sm .nav-btn::before {
border-top: 1px dotted rgba(255, 255, 255, 0.1);
content: "";
left: 0;
position: absolute;
top: 0;
width: 100%; }
.nav-dropdown-sm .nav-btn + .nav-btn {
padding-top: 0.625rem; }
.nav-dropdown-sm .nav-btn + .nav-btn::before {
display: none; }
.nav-dropdown-sm .btn {
padding: 0.625rem 0; }
.nav-dropdown-sm .dropdown-toggle[data-toggle="dropdown-submenu"]::after {
margin-left: .25rem;
border-top: 0.35em solid;
border-right: 0.35em solid transparent;
border-left: 0.35em solid transparent;
border-bottom: 0; }
.nav-dropdown-sm .dropdown-toggle[data-toggle="dropdown-submenu"][aria-expanded="true"]::after {
border-top: 0;
border-right: 0.35em solid transparent;
border-left: 0.35em solid transparent;
border-bottom: 0.35em solid; }
.nav-dropdown-sm .dropdown-menu {
margin: 0;
padding: 0;
position: relative;
top: 0;
left: 0;
width: 100%;
border: 0;
float: none;
border-radius: 0;
background: none; }
.nav-dropdown-sm .dropdown-submenu {
left: 100%;
margin-left: 0.125rem;
margin-top: -1.25rem;
top: 0; }
.navbar-toggleable-sm .nav-dropdown .dropdown-menu {
position: absolute; }
.navbar-toggleable-sm .nav-dropdown .dropdown-submenu {
left: 100%;
margin-left: 0.125rem;
margin-top: -1.25rem;
top: 0; }
.navbar-toggleable-sm.opened .nav-dropdown .dropdown-menu {
position: relative; }
.navbar-toggleable-sm.opened .nav-dropdown .dropdown-submenu {
left: 0;
margin-left: 00rem;
margin-top: 0rem;
top: 0; }
.is-builder .nav-dropdown.collapsing {
transition: none !important; }

View File

@ -0,0 +1,8 @@
var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.SIMPLE_FROUND_POLYFILL=!1;$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(b,c,a){b!=Array.prototype&&b!=Object.prototype&&(b[c]=a.value)};$jscomp.getGlobal=function(b){return"undefined"!=typeof window&&window===b?b:"undefined"!=typeof global&&null!=global?global:b};$jscomp.global=$jscomp.getGlobal(this);
$jscomp.polyfill=function(b,c,a,d){if(c){a=$jscomp.global;b=b.split(".");for(d=0;d<b.length-1;d++){var f=b[d];f in a||(a[f]={});a=a[f]}b=b[b.length-1];d=a[b];c=c(d);c!=d&&null!=c&&$jscomp.defineProperty(a,b,{configurable:!0,writable:!0,value:c})}};
$jscomp.polyfill("Array.from",function(b){return b?b:function(b,a,d){a=null!=a?a:function(a){return a};var f=[],e="undefined"!=typeof Symbol&&Symbol.iterator&&b[Symbol.iterator];if("function"==typeof e){b=e.call(b);for(var c=0;!(e=b.next()).done;)f.push(a.call(d,e.value,c++))}else for(e=b.length,c=0;c<e;c++)f.push(a.call(d,b[c],c));return f}},"es6","es3");
(function(){function b(a){"resize"===a.type&&(document.body.classList.remove("navbar-dropdown-open"),document.querySelector(".navbar-dropdown").querySelector(".navbar-collapse").classList.remove("show"),document.querySelector(".navbar-dropdown").classList.remove("opened"),Array.from(document.querySelector(".navbar-dropdown").querySelectorAll('.navbar-toggler[aria-expanded="true"]')).forEach(function(a){var b=a.querySelector(a.getAttribute("data-target"));b&&(b.classList.remove("in"),b.setAttribute("aria-expanded",
"false"),a.setAttribute("aria-expanded","false"))}));var b=document.documentElement.scrollTop;Array.from(document.querySelectorAll(".navbar-dropdown")).forEach(function(a){a.matches(".navbar-fixed-top")&&(a.matches(".transparent")&&!a.classList.contains("opened")&&(0<b?a.classList.remove("bg-color"):a.classList.add("bg-color")),0<b?a.classList.add("navbar-short"):a.classList.remove("navbar-short"))})}var c;["scroll","resize"].forEach(function(a){document.addEventListener(a,function(a){clearTimeout(c);
c=setTimeout(function(){b(a)},10)})});["show.bs.collapse","hide.bs.collapse"].forEach(function(a){document.addEventListener(a,function(b){if(b=b.target.closest(".navbar-dropdown"))"show.bs.collapse"===a?(document.body.classList.add("navbar-dropdown-open"),b.classList.add("opened")):(document.body.classList.remove("navbar-dropdown-open"),b.classList.remove("opened"),window.dispatchEvent(new Event("scroll.bs.navbar-dropdown.data-api")),b.dispatchEvent(new Event("collapse.bs.navbar-dropdown")))})});
document.querySelector("html").classList.contains("is-builder")||document.addEventListener("click",function(a){a=a.target;if(!a.classList.contains("nav-link")&&!a.parentNode.classList.contains("nav-link")){var b=document.querySelector("#navbarSupportedContent"),c=document.querySelector(".navbar-dropdown"),e=b.classList.contains("show"),g=a.closest(".nav-item a:not(.dropdown-toggle)");c=c.classList.contains("collapsed");(window.matchMedia("(max-width: 991px)").matches||c)&&(e&&!a.closest(".navbar-collapse")||
g)&&new bootstrap.Collapse(b)}});document.addEventListener("collapse.bs.nav-dropdown",function(a){(a=a.relatedTarget.closest(".navbar-dropdown"))&&(a=a.querySelector('.navbar-toggler[aria-expanded="true"]'))&&a.dispatchEvent(new Event("click"))});document.querySelectorAll(".nav-link.dropdown-toggle").forEach(function(a){a.addEventListener("click",function(a){a.preventDefault();a.target.parentNode.classList.toggle("open")})})})();

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 KiB

View File

@ -0,0 +1 @@
{"+GQdJ/FkTHHhtIo5SJpbjw==":"logo-96x43.png","sNnwj6dliedFMQPUjtqZKA==":"logo-1-96x43.png","FshdhnAXWIc3Sivjcd0w7w==":"logo-2-96x43.png","o6j4Z3r46yoaBgNIONzS4A==":"logo-3-96x43.png","gakqOFXqLTkOgBY/bPwdWw==":"logo-4-96x43.png","erPZweO4Ec5GB5uI98xycQ==":"ecosostenibilita-1000x500.jpeg","XL8v257g6FjjbOw+0BL9KA==":"air-pollution-1679x944.jpg","BOA7b8UnSidGomEe8QOuuQ==":"the%20expansion%20of%20cloud%20applications%20has%20added%20to...-766x476.png"}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
.jarallax {
position: relative;
z-index: 0;
}
.jarallax > .jarallax-img {
position: absolute;
object-fit: cover;
/* support for plugin https://github.com/bfred-it/object-fit-images */
font-family: 'object-fit: cover;';
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
}

View File

@ -0,0 +1,45 @@
/*
Name : Just Another Parallax [Jarallax]
Version : 1.12.7
Author : nK <https://nkdev.info>
GitHub : https://github.com/nk-o/jarallax
*/
var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.SIMPLE_FROUND_POLYFILL=!1;$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(a,d,b){a!=Array.prototype&&a!=Object.prototype&&(a[d]=b.value)};$jscomp.getGlobal=function(a){return"undefined"!=typeof window&&window===a?a:"undefined"!=typeof global&&null!=global?global:a};$jscomp.global=$jscomp.getGlobal(this);
$jscomp.polyfill=function(a,d,b,e){if(d){b=$jscomp.global;a=a.split(".");for(e=0;e<a.length-1;e++){var g=a[e];g in b||(b[g]={});b=b[g]}a=a[a.length-1];e=b[a];d=d(e);d!=e&&null!=d&&$jscomp.defineProperty(b,a,{configurable:!0,writable:!0,value:d})}};
$jscomp.polyfill("Array.from",function(a){return a?a:function(a,b,e){b=null!=b?b:function(a){return a};var d=[],l="undefined"!=typeof Symbol&&Symbol.iterator&&a[Symbol.iterator];if("function"==typeof l){a=l.call(a);for(var q=0;!(l=a.next()).done;)d.push(b.call(e,l.value,q++))}else for(l=a.length,q=0;q<l;q++)d.push(b.call(e,a[q],q));return d}},"es6","es3");$jscomp.arrayIteratorImpl=function(a){var d=0;return function(){return d<a.length?{done:!1,value:a[d++]}:{done:!0}}};$jscomp.arrayIterator=function(a){return{next:$jscomp.arrayIteratorImpl(a)}};
$jscomp.SYMBOL_PREFIX="jscomp_symbol_";$jscomp.initSymbol=function(){$jscomp.initSymbol=function(){};$jscomp.global.Symbol||($jscomp.global.Symbol=$jscomp.Symbol)};$jscomp.SymbolClass=function(a,d){this.$jscomp$symbol$id_=a;$jscomp.defineProperty(this,"description",{configurable:!0,writable:!0,value:d})};$jscomp.SymbolClass.prototype.toString=function(){return this.$jscomp$symbol$id_};
$jscomp.Symbol=function(){function a(b){if(this instanceof a)throw new TypeError("Symbol is not a constructor");return new $jscomp.SymbolClass($jscomp.SYMBOL_PREFIX+(b||"")+"_"+d++,b)}var d=0;return a}();
$jscomp.initSymbolIterator=function(){$jscomp.initSymbol();var a=$jscomp.global.Symbol.iterator;a||(a=$jscomp.global.Symbol.iterator=$jscomp.global.Symbol("Symbol.iterator"));"function"!=typeof Array.prototype[a]&&$jscomp.defineProperty(Array.prototype,a,{configurable:!0,writable:!0,value:function(){return $jscomp.iteratorPrototype($jscomp.arrayIteratorImpl(this))}});$jscomp.initSymbolIterator=function(){}};
$jscomp.initSymbolAsyncIterator=function(){$jscomp.initSymbol();var a=$jscomp.global.Symbol.asyncIterator;a||(a=$jscomp.global.Symbol.asyncIterator=$jscomp.global.Symbol("Symbol.asyncIterator"));$jscomp.initSymbolAsyncIterator=function(){}};$jscomp.iteratorPrototype=function(a){$jscomp.initSymbolIterator();a={next:a};a[$jscomp.global.Symbol.iterator]=function(){return this};return a};
$jscomp.iteratorFromArray=function(a,d){$jscomp.initSymbolIterator();a instanceof String&&(a+="");var b=0,e={next:function(){if(b<a.length){var g=b++;return{value:d(g,a[g]),done:!1}}e.next=function(){return{done:!0,value:void 0}};return e.next()}};e[Symbol.iterator]=function(){return e};return e};$jscomp.polyfill("Array.prototype.keys",function(a){return a?a:function(){return $jscomp.iteratorFromArray(this,function(a){return a})}},"es6","es3");
(function(a){function d(e){if(b[e])return b[e].exports;var g=b[e]={i:e,l:!1,exports:{}};a[e].call(g.exports,g,g.exports,d);g.l=!0;return g.exports}var b={};d.m=a;d.c=b;d.d=function(a,b,l){d.o(a,b)||Object.defineProperty(a,b,{enumerable:!0,get:l})};d.r=function(a){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(a,Symbol.toStringTag,{value:"Module"});Object.defineProperty(a,"__esModule",{value:!0})};d.t=function(a,b){b&1&&(a=d(a));if(b&8||b&4&&"object"===typeof a&&a&&a.__esModule)return a;
var g=Object.create(null);d.r(g);Object.defineProperty(g,"default",{enumerable:!0,value:a});if(b&2&&"string"!=typeof a)for(var e in a)d.d(g,e,function(b){return a[b]}.bind(null,e));return g};d.n=function(a){var b=a&&a.__esModule?function(){return a["default"]}:function(){return a};d.d(b,"a",b);return b};d.o=function(a,b){return Object.prototype.hasOwnProperty.call(a,b)};d.p="";return d(d.s=10)})([,,function(a,d){a.exports=function(a){"complete"===document.readyState||"interactive"===document.readyState?
a.call():document.attachEvent?document.attachEvent("onreadystatechange",function(){"interactive"===document.readyState&&a.call()}):document.addEventListener&&document.addEventListener("DOMContentLoaded",a)}},function(a,d,b){d=b(4);a.exports="undefined"!==typeof window?window:"undefined"!==typeof d?d:"undefined"!==typeof self?self:{}},function(a,d){function b(a){"@babel/helpers - typeof";b="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&
"function"===typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a};return b(a)}d=function(){return this}();try{d=d||(new Function("return this"))()}catch(e){"object"===("undefined"===typeof window?"undefined":b(window))&&(d=window)}a.exports=d},,,,,,function(a,d,b){a.exports=b(11)},function(a,d,b){function e(a){"@babel/helpers - typeof";e="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&"function"===typeof Symbol&&
a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a};return e(a)}b.r(d);a=b(2);a=b.n(a);var g=b(3);b.n(g);var l=b(12),q=g.window.jarallax;g.window.jarallax=l["default"];g.window.jarallax.noConflict=function(){g.window.jarallax=q;return this};if("undefined"!==typeof g.jQuery){b=function(){for(var a=arguments.length,b=Array(a),d=0;d<a;d++)b[d]=arguments[d];Array.prototype.unshift.call(b,this);a=l["default"].apply(g.window,b);return"object"!==e(a)?a:this};b.constructor=l["default"].constructor;
var v=g.jQuery.fn.jarallax;g.jQuery.fn.jarallax=b;g.jQuery.fn.jarallax.noConflict=function(){g.jQuery.fn.jarallax=v;return this}}a()(function(){Object(l["default"])(document.querySelectorAll("[data-jarallax]"))})},function(a,d,b){function e(a,f){var h=Array.isArray(a)?a:void 0;if(!h)if(h=a&&("undefined"!==typeof Symbol&&a[Symbol.iterator]||a["@@iterator"]),null==h)h=void 0;else{var c=[],b=!0,d=!1,m;try{for(h=h.call(a);!(b=(m=h.next()).done)&&(c.push(m.value),!f||c.length!==f);b=!0);}catch(B){d=!0;
var e=B}finally{try{if(!b&&null!=h["return"])h["return"]()}finally{if(d)throw e;}}h=c}if(!(m=h))a:{if(a){if("string"===typeof a){m=g(a,f);break a}m=Object.prototype.toString.call(a).slice(8,-1);"Object"===m&&a.constructor&&(m=a.constructor.name);if("Map"===m||"Set"===m){m=Array.from(a);break a}if("Arguments"===m||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(m)){m=g(a,f);break a}}m=void 0}if(!(a=m))throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
return a}function g(a,f){if(null==f||f>a.length)f=a.length;for(var h=0,c=Array(f);h<f;h++)c[h]=a[h];return c}function l(a){"@babel/helpers - typeof";l="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&"function"===typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a};return l(a)}function q(a,f){for(var h=0;h<f.length;h++){var c=f[h];c.enumerable=c.enumerable||!1;c.configurable=!0;"value"in c&&(c.writable=!0);Object.defineProperty(a,
c.key,c)}}function v(a,f,h){f&&q(a.prototype,f);h&&q(a,h);return a}function u(){C?(!t&&document.body&&(t=document.createElement("div"),t.style.cssText="position: fixed; top: -9999px; left: 0; height: 100vh; width: 0;",document.body.appendChild(t)),k=(t?t.clientHeight:0)||p.window.innerHeight||document.documentElement.clientHeight):k=p.window.innerHeight||document.documentElement.clientHeight}function A(a){for(var f=[];null!==a.parentElement;)a=a.parentElement,1===a.nodeType&&f.push(a);return f}function x(){r.length&&
(r.forEach(function(a,f){var h=a.instance;a=a.oldData;var c=h.$item.getBoundingClientRect();c={width:c.width,height:c.height,top:c.top,bottom:c.bottom,wndW:p.window.innerWidth,wndH:k};var b=!a||a.wndW!==c.wndW||a.wndH!==c.wndH||a.width!==c.width||a.height!==c.height;a=b||!a||a.top!==c.top||a.bottom!==c.bottom;r[f].oldData=c;if(b)h.onResize();if(a)h.onScroll()}),p.window.requestAnimationFrame(x))}b.r(d);a=b(2);a=b.n(a);var p=b(3);b.n(p);var n=p.window.navigator,D=-1<n.userAgent.indexOf("MSIE ")||-1<
n.userAgent.indexOf("Trident/")||-1<n.userAgent.indexOf("Edge/"),C=/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(n.userAgent),w=function(){for(var a=["transform","WebkitTransform","MozTransform"],f=document.createElement("div"),h=0;h<a.length;h+=1)if(f&&void 0!==f.style[a[h]])return a[h];return!1}(),t,k;u();p.window.addEventListener("resize",u);p.window.addEventListener("orientationchange",u);p.window.addEventListener("load",u);a()(function(){u({type:"dom-loaded"})});var r=
[],y=0,z=function(){function a(f,h){if(!(this instanceof a))throw new TypeError("Cannot call a class as a function");var c=this;c.instanceID=y;y+=1;c.$item=f;c.defaults={type:"scroll",speed:.5,imgSrc:null,imgElement:".jarallax-img",imgSize:"cover",imgPosition:"50% 50%",imgRepeat:"no-repeat",keepImg:!1,elementInViewport:null,zIndex:-100,disableParallax:!1,disableVideo:!1,videoSrc:null,videoStartTime:0,videoEndTime:0,videoVolume:0,videoLoop:!0,videoPlayOnlyVisible:!0,videoLazyLoading:!0,onScroll:null,
onInit:null,onDestroy:null,onCoverImage:null};var b=c.$item.dataset||{},d={};Object.keys(b).forEach(function(a){var f=a.substr(0,1).toLowerCase()+a.substr(1);f&&"undefined"!==typeof c.defaults[f]&&(d[f]=b[a])});c.options=c.extend({},c.defaults,d,h);c.pureOptions=c.extend({},c.options);Object.keys(c.options).forEach(function(a){"true"===c.options[a]?c.options[a]=!0:"false"===c.options[a]&&(c.options[a]=!1)});c.options.speed=Math.min(2,Math.max(-1,parseFloat(c.options.speed)));"string"===typeof c.options.disableParallax&&
(c.options.disableParallax=new RegExp(c.options.disableParallax));if(c.options.disableParallax instanceof RegExp){var g=c.options.disableParallax;c.options.disableParallax=function(){return g.test(n.userAgent)}}"function"!==typeof c.options.disableParallax&&(c.options.disableParallax=function(){return!1});"string"===typeof c.options.disableVideo&&(c.options.disableVideo=new RegExp(c.options.disableVideo));if(c.options.disableVideo instanceof RegExp){var m=c.options.disableVideo;c.options.disableVideo=
function(){return m.test(n.userAgent)}}"function"!==typeof c.options.disableVideo&&(c.options.disableVideo=function(){return!1});(f=c.options.elementInViewport)&&"object"===l(f)&&"undefined"!==typeof f.length&&(f=e(f,1)[0]);f instanceof Element||(f=null);c.options.elementInViewport=f;c.image={src:c.options.imgSrc||null,$container:null,useImgTag:!1,position:/iPad|iPhone|iPod|Android/.test(n.userAgent)?"absolute":"fixed"};c.initImg()&&c.canInitParallax()&&c.init()}v(a,[{key:"css",value:function(a,b){if("string"===
typeof b)return p.window.getComputedStyle(a).getPropertyValue(b);b.transform&&w&&(b[w]=b.transform);Object.keys(b).forEach(function(c){a.style[c]=b[c]});return a}},{key:"extend",value:function(a){for(var b=arguments.length,c=Array(1<b?b-1:0),f=1;f<b;f++)c[f-1]=arguments[f];a=a||{};Object.keys(c).forEach(function(b){c[b]&&Object.keys(c[b]).forEach(function(f){a[f]=c[b][f]})});return a}},{key:"getWindowData",value:function(){return{width:p.window.innerWidth||document.documentElement.clientWidth,height:k,
y:document.documentElement.scrollTop}}},{key:"initImg",value:function(){var a=this.options.imgElement;a&&"string"===typeof a&&(a=this.$item.querySelector(a));a instanceof Element||(this.options.imgSrc?(a=new Image,a.src=this.options.imgSrc):a=null);a&&(this.options.keepImg?this.image.$item=a.cloneNode(!0):(this.image.$item=a,this.image.$itemParent=a.parentNode),this.image.useImgTag=!0);if(this.image.$item)return!0;null===this.image.src&&(this.image.src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
this.image.bgImage=this.css(this.$item,"background-image"));return!(!this.image.bgImage||"none"===this.image.bgImage)}},{key:"canInitParallax",value:function(){return w&&!this.options.disableParallax()}},{key:"init",value:function(){var a={position:"absolute",top:0,left:0,width:"100%",height:"100%",overflow:"hidden"},b={pointerEvents:"none",transformStyle:"preserve-3d",backfaceVisibility:"hidden",willChange:"transform,opacity"};if(!this.options.keepImg){var c=this.$item.getAttribute("style");c&&this.$item.setAttribute("data-jarallax-original-styles",
c);this.image.useImgTag&&(c=this.image.$item.getAttribute("style"))&&this.image.$item.setAttribute("data-jarallax-original-styles",c)}"static"===this.css(this.$item,"position")&&this.css(this.$item,{position:"relative"});"auto"===this.css(this.$item,"z-index")&&this.css(this.$item,{zIndex:0});this.image.$container=document.createElement("div");this.css(this.image.$container,a);this.css(this.image.$container,{"z-index":this.options.zIndex});D&&this.css(this.image.$container,{opacity:.9999});this.image.$container.setAttribute("id",
"jarallax-container-".concat(this.instanceID));this.$item.appendChild(this.image.$container);this.image.useImgTag?b=this.extend({"object-fit":this.options.imgSize,"object-position":this.options.imgPosition,"font-family":"object-fit: ".concat(this.options.imgSize,"; object-position: ").concat(this.options.imgPosition,";"),"max-width":"none"},a,b):(this.image.$item=document.createElement("div"),this.image.src&&(b=this.extend({"background-position":this.options.imgPosition,"background-size":this.options.imgSize,
"background-repeat":this.options.imgRepeat,"background-image":this.image.bgImage||'url("'.concat(this.image.src,'")')},a,b)));if("opacity"===this.options.type||"scale"===this.options.type||"scale-opacity"===this.options.type||1===this.options.speed)this.image.position="absolute";"fixed"===this.image.position&&(a=A(this.$item).filter(function(a){a=p.window.getComputedStyle(a);var c=a["-webkit-transform"]||a["-moz-transform"]||a.transform,b=/(auto|scroll)/;return c&&"none"!==c||b.test(a.overflow+a["overflow-y"]+
a["overflow-x"])}),this.image.position=a.length?"absolute":"fixed");b.position=this.image.position;this.css(this.image.$item,b);this.image.$container.appendChild(this.image.$item);this.onResize();this.onScroll(!0);this.options.onInit&&this.options.onInit.call(this);"none"!==this.css(this.$item,"background-image")&&this.css(this.$item,{"background-image":"none"});this.addToParallaxList()}},{key:"addToParallaxList",value:function(){r.push({instance:this});1===r.length&&p.window.requestAnimationFrame(x)}},
{key:"removeFromParallaxList",value:function(){var a=this;r.forEach(function(b,c){b.instance.instanceID===a.instanceID&&r.splice(c,1)})}},{key:"destroy",value:function(){this.removeFromParallaxList();var a=this.$item.getAttribute("data-jarallax-original-styles");this.$item.removeAttribute("data-jarallax-original-styles");a?this.$item.setAttribute("style",a):this.$item.removeAttribute("style");if(this.image.useImgTag){var b=this.image.$item.getAttribute("data-jarallax-original-styles");this.image.$item.removeAttribute("data-jarallax-original-styles");
b?this.image.$item.setAttribute("style",a):this.image.$item.removeAttribute("style");this.image.$itemParent&&this.image.$itemParent.appendChild(this.image.$item)}this.$clipStyles&&this.$clipStyles.parentNode.removeChild(this.$clipStyles);this.image.$container&&this.image.$container.parentNode.removeChild(this.image.$container);this.options.onDestroy&&this.options.onDestroy.call(this);delete this.$item.jarallax}},{key:"clipContainer",value:function(){if("fixed"===this.image.position){var a=this.image.$container.getBoundingClientRect(),
b=a.width;a=a.height;this.$clipStyles||(this.$clipStyles=document.createElement("style"),this.$clipStyles.setAttribute("type","text/css"),this.$clipStyles.setAttribute("id","jarallax-clip-".concat(this.instanceID)),(document.head||document.getElementsByTagName("head")[0]).appendChild(this.$clipStyles));b="#jarallax-container-".concat(this.instanceID," {\n clip: rect(0 ").concat(b,"px ").concat(a,"px 0);\n clip: rect(0, ").concat(b,"px, ").concat(a,"px, 0);\n -webkit-clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);\n clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);\n }");
this.$clipStyles.styleSheet?this.$clipStyles.styleSheet.cssText=b:this.$clipStyles.innerHTML=b}}},{key:"coverImage",value:function(){var a=this.image.$container.getBoundingClientRect(),b=a.height,c=this.options.speed,d="scroll"===this.options.type||"scroll-opacity"===this.options.type,e=0,g=b;d&&(0>c?(e=c*Math.max(b,k),k<b&&(e-=c*(b-k))):e=c*(b+k),g=1<c?Math.abs(e-k):0>c?e/c+Math.abs(e):g+(k-b)*(1-c),e/=2);this.parallaxScrollDistance=e;b=d?(k-g)/2:(b-g)/2;this.css(this.image.$item,{height:"".concat(g,
"px"),marginTop:"".concat(b,"px"),left:"fixed"===this.image.position?"".concat(a.left,"px"):"0",width:"".concat(a.width,"px")});this.options.onCoverImage&&this.options.onCoverImage.call(this);return{image:{height:g,marginTop:b},container:a}}},{key:"isVisible",value:function(){return this.isElementInViewport||!1}},{key:"onScroll",value:function(a){var b=this.$item.getBoundingClientRect(),c=b.top,d=b.height,f={},e=b;this.options.elementInViewport&&(e=this.options.elementInViewport.getBoundingClientRect());
this.isElementInViewport=0<=e.bottom&&0<=e.right&&e.top<=k&&e.left<=p.window.innerWidth;if(a||this.isElementInViewport){a=Math.max(0,c);e=Math.max(0,d+c);var g=Math.max(0,-c),m=Math.max(0,c+d-k),l=Math.max(0,d-(c+d-k)),q=Math.max(0,-c+k-d),r=1-(k-c)/(k+d)*2,n=1;d<k?n=1-(g||m)/d:e<=k?n=e/k:l<=k&&(n=l/k);if("opacity"===this.options.type||"scale-opacity"===this.options.type||"scroll-opacity"===this.options.type)f.transform="translate3d(0,0,0)",f.opacity=n;if("scale"===this.options.type||"scale-opacity"===
this.options.type)d=1,d=0>this.options.speed?d-this.options.speed*n:d+this.options.speed*(1-n),f.transform="scale(".concat(d,") translate3d(0,0,0)");if("scroll"===this.options.type||"scroll-opacity"===this.options.type)d=this.parallaxScrollDistance*r,"absolute"===this.image.position&&(d-=c),f.transform="translate3d(0,".concat(d,"px,0)");this.css(this.image.$item,f);this.options.onScroll&&this.options.onScroll.call(this,{section:b,beforeTop:a,beforeTopEnd:e,afterTop:g,beforeBottom:m,beforeBottomEnd:l,
afterBottom:q,visiblePercent:n,fromViewportCenter:r})}}},{key:"onResize",value:function(){this.coverImage();this.clipContainer()}}]);return a}();b=function(a,b){if("object"===("undefined"===typeof HTMLElement?"undefined":l(HTMLElement))?a instanceof HTMLElement:a&&"object"===l(a)&&null!==a&&1===a.nodeType&&"string"===typeof a.nodeName)a=[a];for(var d=a.length,c=0,e,f=arguments.length,g=Array(2<f?f-2:0),k=2;k<f;k++)g[k-2]=arguments[k];for(c;c<d;c+=1)if("object"===l(b)||"undefined"===typeof b?a[c].jarallax||
(a[c].jarallax=new z(a[c],b)):a[c].jarallax&&(e=a[c].jarallax[b].apply(a[c].jarallax,g)),"undefined"!==typeof e)return e;return a};b.constructor=z;d["default"]=b}]);

View File

@ -0,0 +1,15 @@
(function(){function C(){if(!D&&document.body){D=!0;var a=document.body,b=document.documentElement,d=window.innerHeight,c=a.scrollHeight;l=0<=document.compatMode.indexOf("CSS")?b:a;m=a;f.keyboardSupport&&window.addEventListener("keydown",M,!1);if(top!=self)v=!0;else if(ca&&c>d&&(a.offsetHeight<=d||b.offsetHeight<=d)){var e=document.createElement("div");e.style.cssText="position:absolute; z-index:-10000; top:0; left:0; right:0; height:"+l.scrollHeight+"px";document.body.appendChild(e);var g;w=function(){g||
(g=setTimeout(function(){e.style.height="0";e.style.height=l.scrollHeight+"px";g=null},500))};setTimeout(w,10);window.addEventListener("resize",w,!1);z=new da(w);z.observe(a,{attributes:!0,childList:!0,characterData:!1});l.offsetHeight<=d&&(d=document.createElement("div"),d.style.clear="both",a.appendChild(d))}f.fixedBackground||(a.style.backgroundAttachment="scroll",b.style.backgroundAttachment="scroll")}}function N(a,b,d){ea(b,d);if(1!=f.accelerationMax){var c=Date.now()-E;c<f.accelerationDelta&&
(c=(1+50/c)/2,1<c&&(c=Math.min(c,f.accelerationMax),b*=c,d*=c));E=Date.now()}t.push({x:b,y:d,lastX:0>b?.99:-.99,lastY:0>d?.99:-.99,start:Date.now()});if(!F){c=O();var e=a===c||a===document.body;null==a.$scrollBehavior&&fa(a)&&(a.$scrollBehavior=a.style.scrollBehavior,a.style.scrollBehavior="auto");var g=function(c){c=Date.now();for(var k=0,l=0,h=0;h<t.length;h++){var n=t[h],p=c-n.start,m=p>=f.animationTime,q=m?1:p/f.animationTime;f.pulseAlgorithm&&(p=q,1<=p?q=1:0>=p?q=0:(1==f.pulseNormalize&&(f.pulseNormalize/=
P(1)),q=P(p)));p=n.x*q-n.lastX>>0;q=n.y*q-n.lastY>>0;k+=p;l+=q;n.lastX+=p;n.lastY+=q;m&&(t.splice(h,1),h--)}e?window.scrollBy(k,l):(k&&(a.scrollLeft+=k),l&&(a.scrollTop+=l));b||d||(t=[]);t.length?Q(g,a,1E3/f.frameRate+1):(F=!1,null!=a.$scrollBehavior&&(a.style.scrollBehavior=a.$scrollBehavior,a.$scrollBehavior=null))};Q(g,a,0);F=!0}}function R(a){D||C();var b=a.target;if(a.defaultPrevented||a.ctrlKey||r(m,"embed")||r(b,"embed")&&/\.pdf/i.test(b.src)||r(m,"object")||b.shadowRoot)return!0;var d=-a.wheelDeltaX||
a.deltaX||0,c=-a.wheelDeltaY||a.deltaY||0;ha&&(a.wheelDeltaX&&x(a.wheelDeltaX,120)&&(d=a.wheelDeltaX/Math.abs(a.wheelDeltaX)*-120),a.wheelDeltaY&&x(a.wheelDeltaY,120)&&(c=a.wheelDeltaY/Math.abs(a.wheelDeltaY)*-120));d||c||(c=-a.wheelDelta||0);1===a.deltaMode&&(d*=40,c*=40);b=S(b);if(!b)return v&&G?(Object.defineProperty(a,"target",{value:window.frameElement}),parent.wheel(a)):!0;if(ia(c))return!0;1.2<Math.abs(d)&&(d*=f.stepSize/120);1.2<Math.abs(c)&&(c*=f.stepSize/120);N(b,d,c);a.preventDefault();
T()}function M(a){var b=a.target,d=a.ctrlKey||a.altKey||a.metaKey||a.shiftKey&&a.keyCode!==g.spacebar;document.body.contains(m)||(m=document.activeElement);var c=/^(textarea|select|embed|object)$/i,e=/^(button|submit|radio|checkbox|file|color|image)$/i;if(!(c=a.defaultPrevented||c.test(b.nodeName)||r(b,"input")&&!e.test(b.type)||r(m,"video"))){c=a.target;var h=!1;if(-1!=document.URL.indexOf("www.youtube.com/watch")){do if(h=c.classList&&c.classList.contains("html5-video-controls"))break;while(c=c.parentNode)
}c=h}if(c||b.isContentEditable||d||(r(b,"button")||r(b,"input")&&e.test(b.type))&&a.keyCode===g.spacebar||r(b,"input")&&"radio"==b.type&&ja[a.keyCode])return!0;c=b=0;d=S(m);if(!d)return v&&G?parent.keydown(a):!0;e=d.clientHeight;d==document.body&&(e=window.innerHeight);switch(a.keyCode){case g.up:c=-f.arrowScroll;break;case g.down:c=f.arrowScroll;break;case g.spacebar:c=a.shiftKey?1:-1;c=-c*e*.9;break;case g.pageup:c=.9*-e;break;case g.pagedown:c=.9*e;break;case g.home:d==document.body&&document.scrollingElement&&
(d=document.scrollingElement);c=-d.scrollTop;break;case g.end:e=d.scrollHeight-d.scrollTop-e;c=0<e?e+10:0;break;case g.left:b=-f.arrowScroll;break;case g.right:b=f.arrowScroll;break;default:return!0}N(d,b,c);a.preventDefault();T()}function U(a){m=a.target}function T(){clearTimeout(V);V=setInterval(function(){W=H=A={}},1E3)}function I(a,b,d){d=d?W:H;for(var c=a.length;c--;)d[J(a[c])]=b;return b}function S(a){var b=[],d=document.body,c=l.scrollHeight;do{var e=H[J(a)];if(e)return I(b,e);b.push(a);if(c===
a.scrollHeight){if(e=X(l)&&X(d)||Y(l),v&&l.clientHeight+10<l.scrollHeight||!v&&e)return I(b,O())}else if(a.clientHeight+10<a.scrollHeight&&Y(a))return I(b,a)}while(a=a.parentElement)}function X(a){return"hidden"!==getComputedStyle(a,"").getPropertyValue("overflow-y")}function Y(a){a=getComputedStyle(a,"").getPropertyValue("overflow-y");return"scroll"===a||"auto"===a}function fa(a){var b=J(a);null==A[b]&&(a=getComputedStyle(a,"")["scroll-behavior"],A[b]="smooth"==a);return A[b]}function r(a,b){return a&&
(a.nodeName||"").toLowerCase()===b.toLowerCase()}function ea(a,b){a=0<a?1:-1;b=0<b?1:-1;if(B.x!==a||B.y!==b)B.x=a,B.y=b,t=[],E=0}function ia(a){if(a){h.length||(h=[a,a,a]);a=Math.abs(a);h.push(a);h.shift();clearTimeout(Z);Z=setTimeout(function(){try{localStorage.SS_deltaBuffer=h.join(",")}catch(d){}},1E3);var b=120<a&&K(a);b=!K(120)&&!K(100)&&!b;return 50>a?!0:b}}function x(a,b){return Math.floor(a/b)==a/b}function K(a){return x(h[0],a)&&x(h[1],a)&&x(h[2],a)}function P(a){a*=f.pulseScale;if(1>a)var b=
a-(1-Math.exp(-a));else b=Math.exp(-1),a=1-Math.exp(-(a-1)),b+=a*(1-b);return b*f.pulseNormalize}function y(a){for(var b in a)aa.hasOwnProperty(b)&&(f[b]=a[b])}var aa={frameRate:150,animationTime:400,stepSize:100,pulseAlgorithm:!0,pulseScale:4,pulseNormalize:1,accelerationDelta:50,accelerationMax:3,keyboardSupport:!0,arrowScroll:50,fixedBackground:!0,excluded:""},f=aa,v=!1,B={x:0,y:0},D=!1,l=document.documentElement,m,z,w,h=[],Z,ha=/^Mac/.test(navigator.platform),g={left:37,up:38,right:39,down:40,
spacebar:32,pageup:33,pagedown:34,end:35,home:36},ja={37:1,38:1,39:1,40:1},t=[],F=!1,E=Date.now(),J=function(){var a=0;return function(b){return b.uniqueID||(b.uniqueID=a++)}}(),W={},H={},V,A={};if(window.localStorage&&localStorage.SS_deltaBuffer)try{h=localStorage.SS_deltaBuffer.split(",")}catch(a){}var Q=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||function(a,b,d){window.setTimeout(a,d||1E3/60)}}(),da=window.MutationObserver||
window.WebKitMutationObserver||window.MozMutationObserver,O=function(){var a=document.scrollingElement;return function(){if(!a){var b=document.createElement("div");b.style.cssText="height:10000px;width:1px;";document.body.appendChild(b);var d=document.body.scrollTop;window.scrollBy(0,3);a=document.body.scrollTop!=d?document.body:document.documentElement;window.scrollBy(0,-3);document.body.removeChild(b)}return a}}(),k=window.navigator.userAgent,u=/Edge/.test(k),G=/chrome/i.test(k)&&!u;u=/safari/i.test(k)&&
!u;var ka=/mobile/i.test(k),la=/Windows NT 6.1/i.test(k)&&/rv:11/i.test(k),ca=u&&(/Version\/8/i.test(k)||/Version\/9/i.test(k));k=(G||u||la)&&!ka;var ba=!1;try{window.addEventListener("test",null,Object.defineProperty({},"passive",{get:function(){ba=!0}}))}catch(a){}u=ba?{passive:!1}:!1;var L="onwheel"in document.createElement("div")?"wheel":"mousewheel";L&&k&&(window.addEventListener(L,R,u||!1),window.addEventListener("mousedown",U,!1),window.addEventListener("load",C,!1));y.destroy=function(){z&&
z.disconnect();window.removeEventListener(L,R,!1);window.removeEventListener("mousedown",U,!1);window.removeEventListener("keydown",M,!1);window.removeEventListener("resize",w,!1);window.removeEventListener("load",C,!1)};window.SmoothScrollOptions&&y(window.SmoothScrollOptions);"function"===typeof define&&define.amd?define(function(){return y}):"object"==typeof exports?module.exports=y:window.SmoothScroll=y})();

View File

@ -0,0 +1,934 @@
@charset "UTF-8";
@font-face {
font-family: 'Socicon';
src: url('../fonts/socicon.eot');
src: url('../fonts/socicon.eot?#iefix') format('embedded-opentype'),
url('../fonts/socicon.woff2') format('woff2'),
url('../fonts/socicon.ttf') format('truetype'),
url('../fonts/socicon.woff') format('woff'),
url('../fonts/socicon.svg#socicon') format('svg');
font-weight: normal;
font-style: normal;
font-display: swap;
}
[data-icon]:before {
font-family: "socicon" !important;
content: attr(data-icon);
font-style: normal !important;
font-weight: normal !important;
font-variant: normal !important;
text-transform: none !important;
speak: none;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
[class^="socicon-"], [class*=" socicon-"] {
/* use !important to prevent issues with browser extensions that change fonts */
font-family: 'Socicon' !important;
speak: none;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
/* Better Font Rendering =========== */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.socicon-eitaa:before {
content: "\e97c";
}
.socicon-soroush:before {
content: "\e97d";
}
.socicon-bale:before {
content: "\e97e";
}
.socicon-zazzle:before {
content: "\e97b";
}
.socicon-society6:before {
content: "\e97a";
}
.socicon-redbubble:before {
content: "\e979";
}
.socicon-avvo:before {
content: "\e978";
}
.socicon-stitcher:before {
content: "\e977";
}
.socicon-googlehangouts:before {
content: "\e974";
}
.socicon-dlive:before {
content: "\e975";
}
.socicon-vsco:before {
content: "\e976";
}
.socicon-flipboard:before {
content: "\e973";
}
.socicon-ubuntu:before {
content: "\e958";
}
.socicon-artstation:before {
content: "\e959";
}
.socicon-invision:before {
content: "\e95a";
}
.socicon-torial:before {
content: "\e95b";
}
.socicon-collectorz:before {
content: "\e95c";
}
.socicon-seenthis:before {
content: "\e95d";
}
.socicon-googleplaymusic:before {
content: "\e95e";
}
.socicon-debian:before {
content: "\e95f";
}
.socicon-filmfreeway:before {
content: "\e960";
}
.socicon-gnome:before {
content: "\e961";
}
.socicon-itchio:before {
content: "\e962";
}
.socicon-jamendo:before {
content: "\e963";
}
.socicon-mix:before {
content: "\e964";
}
.socicon-sharepoint:before {
content: "\e965";
}
.socicon-tinder:before {
content: "\e966";
}
.socicon-windguru:before {
content: "\e967";
}
.socicon-cdbaby:before {
content: "\e968";
}
.socicon-elementaryos:before {
content: "\e969";
}
.socicon-stage32:before {
content: "\e96a";
}
.socicon-tiktok:before {
content: "\e96b";
}
.socicon-gitter:before {
content: "\e96c";
}
.socicon-letterboxd:before {
content: "\e96d";
}
.socicon-threema:before {
content: "\e96e";
}
.socicon-splice:before {
content: "\e96f";
}
.socicon-metapop:before {
content: "\e970";
}
.socicon-naver:before {
content: "\e971";
}
.socicon-remote:before {
content: "\e972";
}
.socicon-internet:before {
content: "\e957";
}
.socicon-moddb:before {
content: "\e94b";
}
.socicon-indiedb:before {
content: "\e94c";
}
.socicon-traxsource:before {
content: "\e94d";
}
.socicon-gamefor:before {
content: "\e94e";
}
.socicon-pixiv:before {
content: "\e94f";
}
.socicon-myanimelist:before {
content: "\e950";
}
.socicon-blackberry:before {
content: "\e951";
}
.socicon-wickr:before {
content: "\e952";
}
.socicon-spip:before {
content: "\e953";
}
.socicon-napster:before {
content: "\e954";
}
.socicon-beatport:before {
content: "\e955";
}
.socicon-hackerone:before {
content: "\e956";
}
.socicon-hackernews:before {
content: "\e946";
}
.socicon-smashwords:before {
content: "\e947";
}
.socicon-kobo:before {
content: "\e948";
}
.socicon-bookbub:before {
content: "\e949";
}
.socicon-mailru:before {
content: "\e94a";
}
.socicon-gitlab:before {
content: "\e945";
}
.socicon-instructables:before {
content: "\e944";
}
.socicon-portfolio:before {
content: "\e943";
}
.socicon-codered:before {
content: "\e940";
}
.socicon-origin:before {
content: "\e941";
}
.socicon-nextdoor:before {
content: "\e942";
}
.socicon-udemy:before {
content: "\e93f";
}
.socicon-livemaster:before {
content: "\e93e";
}
.socicon-crunchbase:before {
content: "\e93b";
}
.socicon-homefy:before {
content: "\e93c";
}
.socicon-calendly:before {
content: "\e93d";
}
.socicon-realtor:before {
content: "\e90f";
}
.socicon-tidal:before {
content: "\e910";
}
.socicon-qobuz:before {
content: "\e911";
}
.socicon-natgeo:before {
content: "\e912";
}
.socicon-mastodon:before {
content: "\e913";
}
.socicon-unsplash:before {
content: "\e914";
}
.socicon-homeadvisor:before {
content: "\e915";
}
.socicon-angieslist:before {
content: "\e916";
}
.socicon-codepen:before {
content: "\e917";
}
.socicon-slack:before {
content: "\e918";
}
.socicon-openaigym:before {
content: "\e919";
}
.socicon-logmein:before {
content: "\e91a";
}
.socicon-fiverr:before {
content: "\e91b";
}
.socicon-gotomeeting:before {
content: "\e91c";
}
.socicon-aliexpress:before {
content: "\e91d";
}
.socicon-guru:before {
content: "\e91e";
}
.socicon-appstore:before {
content: "\e91f";
}
.socicon-homes:before {
content: "\e920";
}
.socicon-zoom:before {
content: "\e921";
}
.socicon-alibaba:before {
content: "\e922";
}
.socicon-craigslist:before {
content: "\e923";
}
.socicon-wix:before {
content: "\e924";
}
.socicon-redfin:before {
content: "\e925";
}
.socicon-googlecalendar:before {
content: "\e926";
}
.socicon-shopify:before {
content: "\e927";
}
.socicon-freelancer:before {
content: "\e928";
}
.socicon-seedrs:before {
content: "\e929";
}
.socicon-bing:before {
content: "\e92a";
}
.socicon-doodle:before {
content: "\e92b";
}
.socicon-bonanza:before {
content: "\e92c";
}
.socicon-squarespace:before {
content: "\e92d";
}
.socicon-toptal:before {
content: "\e92e";
}
.socicon-gust:before {
content: "\e92f";
}
.socicon-ask:before {
content: "\e930";
}
.socicon-trulia:before {
content: "\e931";
}
.socicon-loomly:before {
content: "\e932";
}
.socicon-ghost:before {
content: "\e933";
}
.socicon-upwork:before {
content: "\e934";
}
.socicon-fundable:before {
content: "\e935";
}
.socicon-booking:before {
content: "\e936";
}
.socicon-googlemaps:before {
content: "\e937";
}
.socicon-zillow:before {
content: "\e938";
}
.socicon-niconico:before {
content: "\e939";
}
.socicon-toneden:before {
content: "\e93a";
}
.socicon-augment:before {
content: "\e908";
}
.socicon-bitbucket:before {
content: "\e909";
}
.socicon-fyuse:before {
content: "\e90a";
}
.socicon-yt-gaming:before {
content: "\e90b";
}
.socicon-sketchfab:before {
content: "\e90c";
}
.socicon-mobcrush:before {
content: "\e90d";
}
.socicon-microsoft:before {
content: "\e90e";
}
.socicon-pandora:before {
content: "\e907";
}
.socicon-messenger:before {
content: "\e906";
}
.socicon-gamewisp:before {
content: "\e905";
}
.socicon-bloglovin:before {
content: "\e904";
}
.socicon-tunein:before {
content: "\e903";
}
.socicon-gamejolt:before {
content: "\e901";
}
.socicon-trello:before {
content: "\e902";
}
.socicon-spreadshirt:before {
content: "\e900";
}
.socicon-500px:before {
content: "\e000";
}
.socicon-8tracks:before {
content: "\e001";
}
.socicon-airbnb:before {
content: "\e002";
}
.socicon-alliance:before {
content: "\e003";
}
.socicon-amazon:before {
content: "\e004";
}
.socicon-amplement:before {
content: "\e005";
}
.socicon-android:before {
content: "\e006";
}
.socicon-angellist:before {
content: "\e007";
}
.socicon-apple:before {
content: "\e008";
}
.socicon-appnet:before {
content: "\e009";
}
.socicon-baidu:before {
content: "\e00a";
}
.socicon-bandcamp:before {
content: "\e00b";
}
.socicon-battlenet:before {
content: "\e00c";
}
.socicon-mixer:before {
content: "\e00d";
}
.socicon-bebee:before {
content: "\e00e";
}
.socicon-bebo:before {
content: "\e00f";
}
.socicon-behance:before {
content: "\e010";
}
.socicon-blizzard:before {
content: "\e011";
}
.socicon-blogger:before {
content: "\e012";
}
.socicon-buffer:before {
content: "\e013";
}
.socicon-chrome:before {
content: "\e014";
}
.socicon-coderwall:before {
content: "\e015";
}
.socicon-curse:before {
content: "\e016";
}
.socicon-dailymotion:before {
content: "\e017";
}
.socicon-deezer:before {
content: "\e018";
}
.socicon-delicious:before {
content: "\e019";
}
.socicon-deviantart:before {
content: "\e01a";
}
.socicon-diablo:before {
content: "\e01b";
}
.socicon-digg:before {
content: "\e01c";
}
.socicon-discord:before {
content: "\e01d";
}
.socicon-disqus:before {
content: "\e01e";
}
.socicon-douban:before {
content: "\e01f";
}
.socicon-draugiem:before {
content: "\e020";
}
.socicon-dribbble:before {
content: "\e021";
}
.socicon-drupal:before {
content: "\e022";
}
.socicon-ebay:before {
content: "\e023";
}
.socicon-ello:before {
content: "\e024";
}
.socicon-endomodo:before {
content: "\e025";
}
.socicon-envato:before {
content: "\e026";
}
.socicon-etsy:before {
content: "\e027";
}
.socicon-facebook:before {
content: "\e028";
}
.socicon-feedburner:before {
content: "\e029";
}
.socicon-filmweb:before {
content: "\e02a";
}
.socicon-firefox:before {
content: "\e02b";
}
.socicon-flattr:before {
content: "\e02c";
}
.socicon-flickr:before {
content: "\e02d";
}
.socicon-formulr:before {
content: "\e02e";
}
.socicon-forrst:before {
content: "\e02f";
}
.socicon-foursquare:before {
content: "\e030";
}
.socicon-friendfeed:before {
content: "\e031";
}
.socicon-github:before {
content: "\e032";
}
.socicon-goodreads:before {
content: "\e033";
}
.socicon-google:before {
content: "\e034";
}
.socicon-googlescholar:before {
content: "\e035";
}
.socicon-googlegroups:before {
content: "\e036";
}
.socicon-googlephotos:before {
content: "\e037";
}
.socicon-googleplus:before {
content: "\e038";
}
.socicon-grooveshark:before {
content: "\e039";
}
.socicon-hackerrank:before {
content: "\e03a";
}
.socicon-hearthstone:before {
content: "\e03b";
}
.socicon-hellocoton:before {
content: "\e03c";
}
.socicon-heroes:before {
content: "\e03d";
}
.socicon-smashcast:before {
content: "\e03e";
}
.socicon-horde:before {
content: "\e03f";
}
.socicon-houzz:before {
content: "\e040";
}
.socicon-icq:before {
content: "\e041";
}
.socicon-identica:before {
content: "\e042";
}
.socicon-imdb:before {
content: "\e043";
}
.socicon-instagram:before {
content: "\e044";
}
.socicon-issuu:before {
content: "\e045";
}
.socicon-istock:before {
content: "\e046";
}
.socicon-itunes:before {
content: "\e047";
}
.socicon-keybase:before {
content: "\e048";
}
.socicon-lanyrd:before {
content: "\e049";
}
.socicon-lastfm:before {
content: "\e04a";
}
.socicon-line:before {
content: "\e04b";
}
.socicon-linkedin:before {
content: "\e04c";
}
.socicon-livejournal:before {
content: "\e04d";
}
.socicon-lyft:before {
content: "\e04e";
}
.socicon-macos:before {
content: "\e04f";
}
.socicon-mail:before {
content: "\e050";
}
.socicon-medium:before {
content: "\e051";
}
.socicon-meetup:before {
content: "\e052";
}
.socicon-mixcloud:before {
content: "\e053";
}
.socicon-modelmayhem:before {
content: "\e054";
}
.socicon-mumble:before {
content: "\e055";
}
.socicon-myspace:before {
content: "\e056";
}
.socicon-newsvine:before {
content: "\e057";
}
.socicon-nintendo:before {
content: "\e058";
}
.socicon-npm:before {
content: "\e059";
}
.socicon-odnoklassniki:before {
content: "\e05a";
}
.socicon-openid:before {
content: "\e05b";
}
.socicon-opera:before {
content: "\e05c";
}
.socicon-outlook:before {
content: "\e05d";
}
.socicon-overwatch:before {
content: "\e05e";
}
.socicon-patreon:before {
content: "\e05f";
}
.socicon-paypal:before {
content: "\e060";
}
.socicon-periscope:before {
content: "\e061";
}
.socicon-persona:before {
content: "\e062";
}
.socicon-pinterest:before {
content: "\e063";
}
.socicon-play:before {
content: "\e064";
}
.socicon-player:before {
content: "\e065";
}
.socicon-playstation:before {
content: "\e066";
}
.socicon-pocket:before {
content: "\e067";
}
.socicon-qq:before {
content: "\e068";
}
.socicon-quora:before {
content: "\e069";
}
.socicon-raidcall:before {
content: "\e06a";
}
.socicon-ravelry:before {
content: "\e06b";
}
.socicon-reddit:before {
content: "\e06c";
}
.socicon-renren:before {
content: "\e06d";
}
.socicon-researchgate:before {
content: "\e06e";
}
.socicon-residentadvisor:before {
content: "\e06f";
}
.socicon-reverbnation:before {
content: "\e070";
}
.socicon-rss:before {
content: "\e071";
}
.socicon-sharethis:before {
content: "\e072";
}
.socicon-skype:before {
content: "\e073";
}
.socicon-slideshare:before {
content: "\e074";
}
.socicon-smugmug:before {
content: "\e075";
}
.socicon-snapchat:before {
content: "\e076";
}
.socicon-songkick:before {
content: "\e077";
}
.socicon-soundcloud:before {
content: "\e078";
}
.socicon-spotify:before {
content: "\e079";
}
.socicon-stackexchange:before {
content: "\e07a";
}
.socicon-stackoverflow:before {
content: "\e07b";
}
.socicon-starcraft:before {
content: "\e07c";
}
.socicon-stayfriends:before {
content: "\e07d";
}
.socicon-steam:before {
content: "\e07e";
}
.socicon-storehouse:before {
content: "\e07f";
}
.socicon-strava:before {
content: "\e080";
}
.socicon-streamjar:before {
content: "\e081";
}
.socicon-stumbleupon:before {
content: "\e082";
}
.socicon-swarm:before {
content: "\e083";
}
.socicon-teamspeak:before {
content: "\e084";
}
.socicon-teamviewer:before {
content: "\e085";
}
.socicon-technorati:before {
content: "\e086";
}
.socicon-telegram:before {
content: "\e087";
}
.socicon-tripadvisor:before {
content: "\e088";
}
.socicon-tripit:before {
content: "\e089";
}
.socicon-triplej:before {
content: "\e08a";
}
.socicon-tumblr:before {
content: "\e08b";
}
.socicon-twitch:before {
content: "\e08c";
}
.socicon-twitter:before {
content: "\e08d";
}
.socicon-uber:before {
content: "\e08e";
}
.socicon-ventrilo:before {
content: "\e08f";
}
.socicon-viadeo:before {
content: "\e090";
}
.socicon-viber:before {
content: "\e091";
}
.socicon-viewbug:before {
content: "\e092";
}
.socicon-vimeo:before {
content: "\e093";
}
.socicon-vine:before {
content: "\e094";
}
.socicon-vkontakte:before {
content: "\e095";
}
.socicon-warcraft:before {
content: "\e096";
}
.socicon-wechat:before {
content: "\e097";
}
.socicon-weibo:before {
content: "\e098";
}
.socicon-whatsapp:before {
content: "\e099";
}
.socicon-wikipedia:before {
content: "\e09a";
}
.socicon-windows:before {
content: "\e09b";
}
.socicon-wordpress:before {
content: "\e09c";
}
.socicon-wykop:before {
content: "\e09d";
}
.socicon-xbox:before {
content: "\e09e";
}
.socicon-xing:before {
content: "\e09f";
}
.socicon-yahoo:before {
content: "\e0a0";
}
.socicon-yammer:before {
content: "\e0a1";
}
.socicon-yandex:before {
content: "\e0a2";
}
.socicon-yelp:before {
content: "\e0a3";
}
.socicon-younow:before {
content: "\e0a4";
}
.socicon-youtube:before {
content: "\e0a5";
}
.socicon-zapier:before {
content: "\e0a6";
}
.socicon-zerply:before {
content: "\e0a7";
}
.socicon-zomato:before {
content: "\e0a8";
}
.socicon-zynga:before {
content: "\e0a9";
}

Binary file not shown.

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 315 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,965 @@
@charset "UTF-8";
section {
background-color: #ffffff;
}
body {
font-style: normal;
line-height: 1.5;
font-weight: 400;
color: #232323;
position: relative;
}
button {
background-color: transparent;
border-color: transparent;
}
.embla__button,
.carousel-control {
background-color: #edefea !important;
opacity: 0.8 !important;
color: #464845 !important;
border-color: #edefea !important;
}
.carousel .close,
.modalWindow .close {
background-color: #edefea !important;
color: #464845 !important;
border-color: #edefea !important;
opacity: 0.8 !important;
}
.carousel .close:hover,
.modalWindow .close:hover {
opacity: 1 !important;
}
.carousel-indicators li {
background-color: #edefea !important;
border: 2px solid #464845 !important;
}
.carousel-indicators li:hover,
.carousel-indicators li:active {
opacity: 0.8 !important;
}
.embla__button:hover,
.carousel-control:hover {
background-color: #edefea !important;
opacity: 1 !important;
}
.modalWindow-video-container {
height: 80%;
}
section,
.container,
.container-fluid {
position: relative;
word-wrap: break-word;
}
a.mbr-iconfont:hover {
text-decoration: none;
}
.article .lead p,
.article .lead ul,
.article .lead ol,
.article .lead pre,
.article .lead blockquote {
margin-bottom: 0;
}
a {
font-style: normal;
font-weight: 400;
cursor: pointer;
}
a, a:hover {
text-decoration: none;
}
.mbr-section-title {
font-style: normal;
line-height: 1.3;
}
.mbr-section-subtitle {
line-height: 1.3;
}
.mbr-text {
font-style: normal;
line-height: 1.7;
}
h1,
h2,
h3,
h4,
h5,
h6,
.display-1,
.display-2,
.display-4,
.display-5,
.display-7,
span,
p,
a {
line-height: 1;
word-break: break-word;
word-wrap: break-word;
font-weight: 400;
}
b,
strong {
font-weight: bold;
}
input:-webkit-autofill, input:-webkit-autofill:hover, input:-webkit-autofill:focus, input:-webkit-autofill:active {
transition-delay: 9999s;
-webkit-transition-property: background-color, color;
transition-property: background-color, color;
}
textarea[type=hidden] {
display: none;
}
section {
background-position: 50% 50%;
background-repeat: no-repeat;
background-size: cover;
}
section .mbr-background-video,
section .mbr-background-video-preview {
position: absolute;
bottom: 0;
left: 0;
right: 0;
top: 0;
}
.hidden {
visibility: hidden;
}
.mbr-z-index20 {
z-index: 20;
}
/*! Base colors */
.mbr-white {
color: #ffffff;
}
.mbr-black {
color: #111111;
}
.mbr-bg-white {
background-color: #ffffff;
}
.mbr-bg-black {
background-color: #000000;
}
/*! Text-aligns */
.align-left {
text-align: left;
}
.align-center {
text-align: center;
}
.align-right {
text-align: right;
}
/*! Font-weight */
.mbr-light {
font-weight: 300;
}
.mbr-regular {
font-weight: 400;
}
.mbr-semibold {
font-weight: 500;
}
.mbr-bold {
font-weight: 700;
}
/*! Media */
.media-content {
flex-basis: 100%;
}
.media-container-row {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
align-content: center;
align-items: start;
}
.media-container-row .media-size-item {
width: 400px;
}
.media-container-column {
display: flex;
flex-direction: column;
flex-wrap: wrap;
justify-content: center;
align-content: center;
align-items: stretch;
}
.media-container-column > * {
width: 100%;
}
@media (min-width: 992px) {
.media-container-row {
flex-wrap: nowrap;
}
}
figure {
margin-bottom: 0;
overflow: hidden;
}
figure[mbr-media-size] {
transition: width 0.1s;
}
img,
iframe {
display: block;
width: 100%;
}
.card {
background-color: transparent;
border: none;
}
.card-box {
width: 100%;
}
.card-img {
text-align: center;
flex-shrink: 0;
-webkit-flex-shrink: 0;
}
.media {
max-width: 100%;
margin: 0 auto;
}
.mbr-figure {
align-self: center;
}
.media-container > div {
max-width: 100%;
}
.mbr-figure img,
.card-img img {
width: 100%;
}
@media (max-width: 991px) {
.media-size-item {
width: auto !important;
}
.media {
width: auto;
}
.mbr-figure {
width: 100% !important;
}
}
/*! Buttons */
.mbr-section-btn {
margin-left: -0.6rem;
margin-right: -0.6rem;
font-size: 0;
}
.btn {
font-weight: 600;
border-width: 1px;
font-style: normal;
margin: 0.6rem 0.6rem;
white-space: normal;
transition: all 0.2s ease-in-out;
display: inline-flex;
align-items: center;
justify-content: center;
word-break: break-word;
}
.btn-sm {
font-weight: 600;
letter-spacing: 0px;
transition: all 0.3s ease-in-out;
}
.btn-md {
font-weight: 600;
letter-spacing: 0px;
transition: all 0.3s ease-in-out;
}
.btn-lg {
font-weight: 600;
letter-spacing: 0px;
transition: all 0.3s ease-in-out;
}
.btn-form {
margin: 0;
}
.btn-form:hover {
cursor: pointer;
}
nav .mbr-section-btn {
margin-left: 0rem;
margin-right: 0rem;
}
/*! Btn icon margin */
.btn .mbr-iconfont,
.btn.btn-sm .mbr-iconfont {
order: 1;
cursor: pointer;
margin-left: 0.5rem;
vertical-align: sub;
}
.btn.btn-md .mbr-iconfont,
.btn.btn-md .mbr-iconfont {
margin-left: 0.8rem;
}
.mbr-regular {
font-weight: 400;
}
.mbr-semibold {
font-weight: 500;
}
.mbr-bold {
font-weight: 700;
}
[type=submit] {
-webkit-appearance: none;
}
/*! Full-screen */
.mbr-fullscreen .mbr-overlay {
min-height: 100vh;
}
.mbr-fullscreen {
display: flex;
display: -moz-flex;
display: -ms-flex;
display: -o-flex;
align-items: center;
min-height: 100vh;
padding-top: 3rem;
padding-bottom: 3rem;
}
/*! Map */
.map {
height: 25rem;
position: relative;
}
.map iframe {
width: 100%;
height: 100%;
}
/*! Scroll to top arrow */
.mbr-arrow-up {
bottom: 25px;
right: 90px;
position: fixed;
text-align: right;
z-index: 5000;
color: #ffffff;
font-size: 22px;
}
.mbr-arrow-up a {
background: rgba(0, 0, 0, 0.2);
border-radius: 50%;
color: #fff;
display: inline-block;
height: 60px;
width: 60px;
border: 2px solid #fff;
outline-style: none !important;
position: relative;
text-decoration: none;
transition: all 0.3s ease-in-out;
cursor: pointer;
text-align: center;
}
.mbr-arrow-up a:hover {
background-color: rgba(0, 0, 0, 0.4);
}
.mbr-arrow-up a i {
line-height: 60px;
}
.mbr-arrow-up-icon {
display: block;
color: #fff;
}
.mbr-arrow-up-icon::before {
content: "";
display: inline-block;
font-family: serif;
font-size: 22px;
line-height: 1;
font-style: normal;
position: relative;
top: 6px;
left: -4px;
transform: rotate(-90deg);
}
/*! Arrow Down */
.mbr-arrow {
position: absolute;
bottom: 45px;
left: 50%;
width: 60px;
height: 60px;
cursor: pointer;
background-color: rgba(80, 80, 80, 0.5);
border-radius: 50%;
transform: translateX(-50%);
}
@media (max-width: 767px) {
.mbr-arrow {
display: none;
}
}
.mbr-arrow > a {
display: inline-block;
text-decoration: none;
outline-style: none;
animation: arrowdown 1.7s ease-in-out infinite;
color: #ffffff;
}
.mbr-arrow > a > i {
position: absolute;
top: -2px;
left: 15px;
font-size: 2rem;
}
#scrollToTop a i::before {
content: "";
position: absolute;
display: block;
border-bottom: 2.5px solid #fff;
border-left: 2.5px solid #fff;
width: 27.8%;
height: 27.8%;
left: 50%;
top: 51%;
transform: translateY(-30%) translateX(-50%) rotate(135deg);
}
@keyframes arrowdown {
0% {
transform: translateY(0px);
}
50% {
transform: translateY(-5px);
}
100% {
transform: translateY(0px);
}
}
@media (max-width: 500px) {
.mbr-arrow-up {
left: 0;
right: 0;
text-align: center;
}
}
/*Gradients animation*/
@keyframes gradient-animation {
from {
background-position: 0% 100%;
animation-timing-function: ease-in-out;
}
to {
background-position: 100% 0%;
animation-timing-function: ease-in-out;
}
}
.bg-gradient {
background-size: 200% 200%;
animation: gradient-animation 5s infinite alternate;
-webkit-animation: gradient-animation 5s infinite alternate;
}
.menu .navbar-brand {
display: -webkit-flex;
}
.menu .navbar-brand span {
display: flex;
display: -webkit-flex;
}
.menu .navbar-brand .navbar-caption-wrap {
display: -webkit-flex;
}
.menu .navbar-brand .navbar-logo img {
display: -webkit-flex;
width: auto;
}
@media (min-width: 768px) and (max-width: 991px) {
.menu .navbar-toggleable-sm .navbar-nav {
display: -ms-flexbox;
}
}
@media (max-width: 991px) {
.menu .navbar-collapse {
max-height: 93.5vh;
}
.menu .navbar-collapse.show {
overflow: auto;
}
}
@media (min-width: 992px) {
.menu .navbar-nav.nav-dropdown {
display: -webkit-flex;
}
.menu .navbar-toggleable-sm .navbar-collapse {
display: -webkit-flex !important;
}
.menu .collapsed .navbar-collapse {
max-height: 93.5vh;
}
.menu .collapsed .navbar-collapse.show {
overflow: auto;
}
}
@media (max-width: 767px) {
.menu .navbar-collapse {
max-height: 80vh;
}
}
.nav-link .mbr-iconfont {
margin-right: 0.5rem;
}
.navbar {
display: -webkit-flex;
-webkit-flex-wrap: wrap;
-webkit-align-items: center;
-webkit-justify-content: space-between;
}
.navbar-collapse {
-webkit-flex-basis: 100%;
-webkit-flex-grow: 1;
-webkit-align-items: center;
}
.nav-dropdown .link {
padding: 0.667em 1.667em !important;
margin: 0 !important;
}
.nav {
display: -webkit-flex;
-webkit-flex-wrap: wrap;
}
.row {
display: -webkit-flex;
-webkit-flex-wrap: wrap;
}
.justify-content-center {
-webkit-justify-content: center;
}
.form-inline {
display: -webkit-flex;
}
.card-wrapper {
-webkit-flex: 1;
}
.carousel-control {
z-index: 10;
display: -webkit-flex;
}
.carousel-controls {
display: -webkit-flex;
}
.media {
display: -webkit-flex;
}
.form-group:focus {
outline: none;
}
.jq-selectbox__select {
padding: 7px 0;
position: relative;
}
.jq-selectbox__dropdown {
overflow: hidden;
border-radius: 10px;
position: absolute;
top: 100%;
left: 0 !important;
width: 100% !important;
}
.jq-selectbox__trigger-arrow {
right: 0;
transform: translateY(-50%);
}
.jq-selectbox li {
padding: 1.07em 0.5em;
}
input[type=range] {
padding-left: 0 !important;
padding-right: 0 !important;
}
.modal-dialog,
.modal-content {
height: 100%;
}
.modal-dialog .carousel-inner {
height: calc(100vh - 1.75rem);
}
@media (max-width: 575px) {
.modal-dialog .carousel-inner {
height: calc(100vh - 1rem);
}
}
.carousel-item {
text-align: center;
}
.carousel-item img {
margin: auto;
}
.navbar-toggler {
align-self: flex-start;
padding: 0.25rem 0.75rem;
font-size: 1.25rem;
line-height: 1;
background: transparent;
border: 1px solid transparent;
border-radius: 0.25rem;
}
.navbar-toggler:focus,
.navbar-toggler:hover {
text-decoration: none;
box-shadow: none;
}
.navbar-toggler-icon {
display: inline-block;
width: 1.5em;
height: 1.5em;
vertical-align: middle;
content: "";
background: no-repeat center center;
background-size: 100% 100%;
}
.navbar-toggler-left {
position: absolute;
left: 1rem;
}
.navbar-toggler-right {
position: absolute;
right: 1rem;
}
.card-img {
width: auto;
}
.menu .navbar.collapsed:not(.beta-menu) {
flex-direction: column;
}
.carousel-item.active,
.carousel-item-next,
.carousel-item-prev {
display: flex;
}
.note-air-layout .dropup .dropdown-menu,
.note-air-layout .navbar-fixed-bottom .dropdown .dropdown-menu {
bottom: initial !important;
}
html,
body {
height: auto;
min-height: 100vh;
}
.dropup .dropdown-toggle::after {
display: none;
}
.form-asterisk {
font-family: initial;
position: absolute;
top: -2px;
font-weight: normal;
}
.form-control-label {
position: relative;
cursor: pointer;
margin-bottom: 0.357em;
padding: 0;
}
.alert {
color: #ffffff;
border-radius: 0;
border: 0;
font-size: 1.1rem;
line-height: 1.5;
margin-bottom: 1.875rem;
padding: 1.25rem;
position: relative;
text-align: center;
}
.alert.alert-form::after {
background-color: inherit;
bottom: -7px;
content: "";
display: block;
height: 14px;
left: 50%;
margin-left: -7px;
position: absolute;
transform: rotate(45deg);
width: 14px;
}
.form-control {
background-color: #ffffff;
background-clip: border-box;
color: #232323;
line-height: 1rem !important;
height: auto;
padding: 1.2rem 2rem;
transition: border-color 0.25s ease 0s;
border: 1px solid transparent !important;
border-radius: 4px;
box-shadow: rgba(0, 0, 0, 0.07) 0px 1px 1px 0px, rgba(0, 0, 0, 0.07) 0px 1px 3px 0px, rgba(0, 0, 0, 0.03) 0px 0px 0px 1px;
}
.form-active .form-control:invalid {
border-color: red;
}
.row > * {
padding-right: 1rem;
padding-left: 1rem;
}
form .row {
margin-left: -0.6rem;
margin-right: -0.6rem;
}
form .row [class*=col] {
padding-left: 0.6rem;
padding-right: 0.6rem;
}
form .mbr-section-btn {
padding-left: 0.6rem;
padding-right: 0.6rem;
}
form .form-check-input {
margin-top: 0.5;
}
textarea.form-control {
line-height: 1.5rem !important;
}
.form-group {
margin-bottom: 1.2rem;
}
.form-control,
form .btn {
min-height: 48px;
}
.gdpr-block label span.textGDPR input[name=gdpr] {
top: 7px;
}
.form-control:focus {
box-shadow: none;
}
:focus {
outline: none;
}
.mbr-overlay {
background-color: #000;
bottom: 0;
left: 0;
opacity: 0.5;
position: absolute;
right: 0;
top: 0;
z-index: 0;
pointer-events: none;
}
blockquote {
font-style: italic;
padding: 3rem;
font-size: 1.09rem;
position: relative;
border-left: 3px solid;
}
ul,
ol,
pre,
blockquote {
margin-bottom: 2.3125rem;
}
.mt-4 {
margin-top: 2rem !important;
}
.mb-4 {
margin-bottom: 2rem !important;
}
.container,
.container-fluid {
padding-left: 16px;
padding-right: 16px;
}
.row {
margin-left: -16px;
margin-right: -16px;
}
.row > [class*=col] {
padding-left: 16px;
padding-right: 16px;
}
@media (min-width: 992px) {
.container-fluid {
padding-left: 32px;
padding-right: 32px;
}
}
@media (max-width: 991px) {
.mbr-container {
padding-left: 16px;
padding-right: 16px;
}
}
.app-video-wrapper > img {
opacity: 1;
}
.app-video-wrapper {
background: transparent;
}
.item {
position: relative;
}
.dropdown-menu .dropdown-menu {
left: 100%;
}
.dropdown-item + .dropdown-menu {
display: none;
}
.dropdown-item:hover + .dropdown-menu,
.dropdown-menu:hover {
display: block;
}
@media (min-aspect-ratio: 16/9) {
.mbr-video-foreground {
height: 300% !important;
top: -100% !important;
}
}
@media (max-aspect-ratio: 16/9) {
.mbr-video-foreground {
width: 300% !important;
left: -100% !important;
}
}.engine {
position: absolute;
text-indent: -2400px;
text-align: center;
padding: 0;
top: 0;
left: -2400px;
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,70 @@
/*
yt-player. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.arrayIteratorImpl=function(a){var b=0;return function(){return b<a.length?{done:!1,value:a[b++]}:{done:!0}}};$jscomp.arrayIterator=function(a){return{next:$jscomp.arrayIteratorImpl(a)}};$jscomp.makeIterator=function(a){var b="undefined"!=typeof Symbol&&Symbol.iterator&&a[Symbol.iterator];return b?b.call(a):$jscomp.arrayIterator(a)};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.SIMPLE_FROUND_POLYFILL=!1;
$jscomp.objectCreate=$jscomp.ASSUME_ES5||"function"==typeof Object.create?Object.create:function(a){var b=function(){};b.prototype=a;return new b};$jscomp.underscoreProtoCanBeSet=function(){var a={a:!0},b={};try{return b.__proto__=a,b.a}catch(c){}return!1};$jscomp.setPrototypeOf="function"==typeof Object.setPrototypeOf?Object.setPrototypeOf:$jscomp.underscoreProtoCanBeSet()?function(a,b){a.__proto__=b;if(a.__proto__!==b)throw new TypeError(a+" is not extensible");return a}:null;
$jscomp.inherits=function(a,b){a.prototype=$jscomp.objectCreate(b.prototype);a.prototype.constructor=a;if($jscomp.setPrototypeOf){var c=$jscomp.setPrototypeOf;c(a,b)}else for(c in b)if("prototype"!=c)if(Object.defineProperties){var d=Object.getOwnPropertyDescriptor(b,c);d&&Object.defineProperty(a,c,d)}else a[c]=b[c];a.superClass_=b.prototype};$jscomp.getGlobal=function(a){return"undefined"!=typeof window&&window===a?a:"undefined"!=typeof global&&null!=global?global:a};$jscomp.global=$jscomp.getGlobal(this);
$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(a,b,c){a!=Array.prototype&&a!=Object.prototype&&(a[b]=c.value)};$jscomp.polyfill=function(a,b,c,d){if(b){c=$jscomp.global;a=a.split(".");for(d=0;d<a.length-1;d++){var e=a[d];e in c||(c[e]={});c=c[e]}a=a[a.length-1];d=c[a];b=b(d);b!=d&&null!=b&&$jscomp.defineProperty(c,a,{configurable:!0,writable:!0,value:b})}};$jscomp.FORCE_POLYFILL_PROMISE=!1;
$jscomp.polyfill("Promise",function(a){function b(){this.batch_=null}function c(a){return a instanceof e?a:new e(function(b,c){b(a)})}if(a&&!$jscomp.FORCE_POLYFILL_PROMISE)return a;b.prototype.asyncExecute=function(a){if(null==this.batch_){this.batch_=[];var b=this;this.asyncExecuteFunction(function(){b.executeBatch_()})}this.batch_.push(a)};var d=$jscomp.global.setTimeout;b.prototype.asyncExecuteFunction=function(a){d(a,0)};b.prototype.executeBatch_=function(){for(;this.batch_&&this.batch_.length;){var a=
this.batch_;this.batch_=[];for(var b=0;b<a.length;++b){var c=a[b];a[b]=null;try{c()}catch(l){this.asyncThrow_(l)}}}this.batch_=null};b.prototype.asyncThrow_=function(a){this.asyncExecuteFunction(function(){throw a;})};var e=function(a){this.state_=0;this.result_=void 0;this.onSettledCallbacks_=[];var b=this.createResolveAndReject_();try{a(b.resolve,b.reject)}catch(h){b.reject(h)}};e.prototype.createResolveAndReject_=function(){function a(a){return function(d){c||(c=!0,a.call(b,d))}}var b=this,c=!1;
return{resolve:a(this.resolveTo_),reject:a(this.reject_)}};e.prototype.resolveTo_=function(a){if(a===this)this.reject_(new TypeError("A Promise cannot resolve to itself"));else if(a instanceof e)this.settleSameAsPromise_(a);else{a:switch(typeof a){case "object":var b=null!=a;break a;case "function":b=!0;break a;default:b=!1}b?this.resolveToNonPromiseObj_(a):this.fulfill_(a)}};e.prototype.resolveToNonPromiseObj_=function(a){var b=void 0;try{b=a.then}catch(h){this.reject_(h);return}"function"==typeof b?
this.settleSameAsThenable_(b,a):this.fulfill_(a)};e.prototype.reject_=function(a){this.settle_(2,a)};e.prototype.fulfill_=function(a){this.settle_(1,a)};e.prototype.settle_=function(a,b){if(0!=this.state_)throw Error("Cannot settle("+a+", "+b+"): Promise already settled in state"+this.state_);this.state_=a;this.result_=b;this.executeOnSettledCallbacks_()};e.prototype.executeOnSettledCallbacks_=function(){if(null!=this.onSettledCallbacks_){for(var a=0;a<this.onSettledCallbacks_.length;++a)f.asyncExecute(this.onSettledCallbacks_[a]);
this.onSettledCallbacks_=null}};var f=new b;e.prototype.settleSameAsPromise_=function(a){var b=this.createResolveAndReject_();a.callWhenSettled_(b.resolve,b.reject)};e.prototype.settleSameAsThenable_=function(a,b){var c=this.createResolveAndReject_();try{a.call(b,c.resolve,c.reject)}catch(l){c.reject(l)}};e.prototype.then=function(a,b){function c(a,b){return"function"==typeof a?function(b){try{d(a(b))}catch(m){f(m)}}:b}var d,f,g=new e(function(a,b){d=a;f=b});this.callWhenSettled_(c(a,d),c(b,f));return g};
e.prototype.catch=function(a){return this.then(void 0,a)};e.prototype.callWhenSettled_=function(a,b){function c(){switch(d.state_){case 1:a(d.result_);break;case 2:b(d.result_);break;default:throw Error("Unexpected state: "+d.state_);}}var d=this;null==this.onSettledCallbacks_?f.asyncExecute(c):this.onSettledCallbacks_.push(c)};e.resolve=c;e.reject=function(a){return new e(function(b,c){c(a)})};e.race=function(a){return new e(function(b,d){for(var e=$jscomp.makeIterator(a),f=e.next();!f.done;f=e.next())c(f.value).callWhenSettled_(b,
d)})};e.all=function(a){var b=$jscomp.makeIterator(a),d=b.next();return d.done?c([]):new e(function(a,e){function f(b){return function(c){g[b]=c;h--;0==h&&a(g)}}var g=[],h=0;do g.push(void 0),h++,c(d.value).callWhenSettled_(f(g.length-1),e),d=b.next();while(!d.done)})};return e},"es6","es3");$jscomp.owns=function(a,b){return Object.prototype.hasOwnProperty.call(a,b)};
$jscomp.polyfill("Object.entries",function(a){return a?a:function(a){var b=[],d;for(d in a)$jscomp.owns(a,d)&&b.push([d,a[d]]);return b}},"es8","es3");$jscomp.assign="function"==typeof Object.assign?Object.assign:function(a,b){for(var c=1;c<arguments.length;c++){var d=arguments[c];if(d)for(var e in d)$jscomp.owns(d,e)&&(a[e]=d[e])}return a};$jscomp.polyfill("Object.assign",function(a){return a||$jscomp.assign},"es6","es3");
$jscomp.findInternal=function(a,b,c){a instanceof String&&(a=String(a));for(var d=a.length,e=0;e<d;e++){var f=a[e];if(b.call(c,f,e,a))return{i:e,v:f}}return{i:-1,v:void 0}};$jscomp.polyfill("Array.prototype.find",function(a){return a?a:function(a,c){return $jscomp.findInternal(this,a,c).v}},"es6","es3");$jscomp.SYMBOL_PREFIX="jscomp_symbol_";$jscomp.initSymbol=function(){$jscomp.initSymbol=function(){};$jscomp.global.Symbol||($jscomp.global.Symbol=$jscomp.Symbol)};
$jscomp.SymbolClass=function(a,b){this.$jscomp$symbol$id_=a;$jscomp.defineProperty(this,"description",{configurable:!0,writable:!0,value:b})};$jscomp.SymbolClass.prototype.toString=function(){return this.$jscomp$symbol$id_};$jscomp.Symbol=function(){function a(c){if(this instanceof a)throw new TypeError("Symbol is not a constructor");return new $jscomp.SymbolClass($jscomp.SYMBOL_PREFIX+(c||"")+"_"+b++,c)}var b=0;return a}();
$jscomp.initSymbolIterator=function(){$jscomp.initSymbol();var a=$jscomp.global.Symbol.iterator;a||(a=$jscomp.global.Symbol.iterator=$jscomp.global.Symbol("Symbol.iterator"));"function"!=typeof Array.prototype[a]&&$jscomp.defineProperty(Array.prototype,a,{configurable:!0,writable:!0,value:function(){return $jscomp.iteratorPrototype($jscomp.arrayIteratorImpl(this))}});$jscomp.initSymbolIterator=function(){}};
$jscomp.initSymbolAsyncIterator=function(){$jscomp.initSymbol();var a=$jscomp.global.Symbol.asyncIterator;a||(a=$jscomp.global.Symbol.asyncIterator=$jscomp.global.Symbol("Symbol.asyncIterator"));$jscomp.initSymbolAsyncIterator=function(){}};$jscomp.iteratorPrototype=function(a){$jscomp.initSymbolIterator();a={next:a};a[$jscomp.global.Symbol.iterator]=function(){return this};return a};
$jscomp.iteratorFromArray=function(a,b){$jscomp.initSymbolIterator();a instanceof String&&(a+="");var c=0,d={next:function(){if(c<a.length){var e=c++;return{value:b(e,a[e]),done:!1}}d.next=function(){return{done:!0,value:void 0}};return d.next()}};d[Symbol.iterator]=function(){return d};return d};$jscomp.polyfill("Array.prototype.entries",function(a){return a?a:function(){return $jscomp.iteratorFromArray(this,function(a,c){return[a,c]})}},"es6","es3");
$jscomp.polyfill("Array.from",function(a){return a?a:function(a,c,d){c=null!=c?c:function(a){return a};var b=[],f="undefined"!=typeof Symbol&&Symbol.iterator&&a[Symbol.iterator];if("function"==typeof f){a=f.call(a);for(var g=0;!(f=a.next()).done;)b.push(c.call(d,f.value,g++))}else for(f=a.length,g=0;g<f;g++)b.push(c.call(d,a[g],g));return b}},"es6","es3");$jscomp.polyfill("Object.is",function(a){return a?a:function(a,c){return a===c?0!==a||1/a===1/c:a!==a&&c!==c}},"es6","es3");
$jscomp.polyfill("Array.prototype.includes",function(a){return a?a:function(a,c){var b=this;b instanceof String&&(b=String(b));var e=b.length;c=c||0;for(0>c&&(c=Math.max(c+e,0));c<e;c++){var f=b[c];if(f===a||Object.is(f,a))return!0}return!1}},"es7","es3");
$jscomp.checkStringArgs=function(a,b,c){if(null==a)throw new TypeError("The 'this' value for String.prototype."+c+" must not be null or undefined");if(b instanceof RegExp)throw new TypeError("First argument to String.prototype."+c+" must not be a regular expression");return a+""};$jscomp.polyfill("String.prototype.includes",function(a){return a?a:function(a,c){return-1!==$jscomp.checkStringArgs(this,a,"includes").indexOf(a,c||0)}},"es6","es3");var EventEmitter=function(){this.events={}};
EventEmitter.prototype.on=function(a,b){"object"!==typeof this.events[a]&&(this.events[a]=[]);this.events[a].push(b)};EventEmitter.prototype.removeListener=function(a,b){"object"===typeof this.events[a]&&(b=this.indexOf(this.events[a],b),-1<b&&this.events[a].splice(b,1))};EventEmitter.prototype.emit=function(a){var b,c=[].slice.call(arguments,1);if("object"===typeof this.events[a]){var d=this.events[a].slice();var e=d.length;for(b=0;b<e;b++)d[b].apply(this,c)}};
EventEmitter.prototype.once=function(a,b){this.on(a,function d(){this.removeListener(a,d);b.apply(this,arguments)})};
var loadScript=function(a,b,c){return new Promise(function(d,e){var f=document.createElement("script");f.async=!0;f.src=a;for(var g=$jscomp.makeIterator(Object.entries(b||{})),k=g.next();!k.done;k=g.next()){var h=$jscomp.makeIterator(k.value);k=h.next().value;h=h.next().value;f.setAttribute(k,h)}f.onload=function(){f.onerror=f.onload=null;d(f)};f.onerror=function(){f.onerror=f.onload=null;e(Error("Failed to load "+a))};(c||document.head||document.getElementsByTagName("head")[0]).appendChild(f)})},
YOUTUBE_IFRAME_API_SRC="https://www.youtube.com/iframe_api",YOUTUBE_STATES={"-1":"unstarted",0:"ended",1:"playing",2:"paused",3:"buffering",5:"cued"},YOUTUBE_ERROR={INVALID_PARAM:2,HTML5_ERROR:5,NOT_FOUND:100,UNPLAYABLE_1:101,UNPLAYABLE_2:150},loadIframeAPICallbacks=[],C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0=function(a,b){EventEmitter.call(this);var c=this;a="string"===typeof a?
document.querySelector(a):a;this._id=a.id?a.id:a.id="ytplayer-"+Math.random().toString(16).slice(2,8);this._opts=Object.assign({width:640,height:360,autoplay:!1,captions:void 0,controls:!0,keyboard:!0,fullscreen:!0,annotations:!0,modestBranding:!1,related:!0,timeupdateFrequency:1E3,playsInline:!0,start:0},b);this.videoId=null;this.destroyed=!1;this._api=null;this._autoplay=!1;this._player=null;this._ready=!1;this._queue=[];this.replayInterval=[];this._interval=null;this._startInterval=this._startInterval.bind(this);
this._stopInterval=this._stopInterval.bind(this);this.on("playing",this._startInterval);this.on("unstarted",this._stopInterval);this.on("ended",this._stopInterval);this.on("paused",this._stopInterval);this.on("buffering",this._stopInterval);this._loadIframeAPI(function(a,b){if(a)return c._destroy(Error("YouTube Iframe API failed to load"));c._api=b;c.videoId&&c.load(c.videoId,c._autoplay,c._start)})};
$jscomp.inherits(C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0,EventEmitter);C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype.indexOf=function(a,b){for(var c=0,d=a.length,e=-1,f=!1;c<d&&!f;)a[c]===b&&(e=c,f=!0),c++;return e};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype.load=function(a,b,c){b=void 0===b?!1:b;c=void 0===c?0:c;this.destroyed||(this._startOptimizeDisplayEvent(),this._optimizeDisplayHandler("center, center"),this.videoId=a,this._autoplay=b,this._start=c,this._api&&(this._player?this._ready&&(b?this._player.loadVideoById(a,c):this._player.cueVideoById(a,c)):this._createPlayer(a)))};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype.play=function(){this._ready?this._player.playVideo():this._queueCommand("play")};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype.replayFrom=function(a){var b=this;!this.replayInterval.find(function(a){return a.iframeParent===b._player.i.parentNode})&&a&&this.replayInterval.push({iframeParent:this._player.i.parentNode,interval:setInterval(function(){if(b._player.getCurrentTime()>=b._player.getDuration()-Number(a)){b.seek(0);for(var c=$jscomp.makeIterator(b.replayInterval.entries()),
d=c.next();!d.done;d=c.next()){d=$jscomp.makeIterator(d.value);var e=d.next().value;d.next();Object.hasOwnProperty.call(b.replayInterval,e)&&(clearInterval(b.replayInterval[e].interval),b.replayInterval.splice(e,1))}}},1E3*Number(a))})};C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype.pause=function(){this._ready?this._player.pauseVideo():this._queueCommand("pause")};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype.stop=function(){this._ready?this._player.stopVideo():this._queueCommand("stop")};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype.seek=function(a){this._ready?this._player.seekTo(a,!0):this._queueCommand("seek",a)};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype._optimizeDisplayHandler=function(a){if(this._player){var b=this._player.i;a=a.split(",");if(b){var c;if(c=b.parentElement){var d=window.getComputedStyle(c);var e=c.clientHeight+parseFloat(d.marginTop,10)+parseFloat(d.marginBottom,10)+parseFloat(d.borderTopWidth,10)+parseFloat(d.borderBottomWidth,10);c=c.clientWidth+parseFloat(d.marginLeft,
10)+parseFloat(d.marginRight,10)+parseFloat(d.borderLeftWidth,10)+parseFloat(d.borderRightWidth,10);e+=80;b.style.width=c+"px";b.style.height=Math.ceil(parseFloat(b.style.width,10)/1.7)+"px";b.style.marginTop=Math.ceil(-((parseFloat(b.style.height,10)-e)/2))+"px";b.style.marginLeft=0;if(d=parseFloat(b.style.height,10)<e)b.style.height=e+"px",b.style.width=Math.ceil(1.7*parseFloat(b.style.height,10))+"px",b.style.marginTop=0,b.style.marginLeft=Math.ceil(-((parseFloat(b.style.width,10)-c)/2))+"px";
for(var f in a)if(a.hasOwnProperty(f))switch(a[f].replace(/ /g,"")){case "top":b.style.marginTop=d?-((parseFloat(b.style.height,10)-e)/2)+"px":0;break;case "bottom":b.style.marginTop=d?0:-(parseFloat(b.style.height,10)-e)+"px";break;case "left":b.style.marginLeft=0;break;case "right":b.style.marginLeft=d?-(parseFloat(b.style.width,10)-c):"0px";break;default:parseFloat(b.style.width,10)>c&&(b.style.marginLeft=-((parseFloat(b.style.width,10)-c)/2)+"px")}}}}};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype.stopResize=function(){window.removeEventListener("resize",this._resizeListener);this._resizeListener=null};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype.stopReplay=function(a){for(var b=$jscomp.makeIterator(this.replayInterval.entries()),c=b.next();!c.done;c=b.next()){c=$jscomp.makeIterator(c.value);var d=c.next().value;c.next();Object.hasOwnProperty.call(this.replayInterval,d)&&a===this.replayInterval[d].iframeParent&&(clearInterval(this.replayInterval[d].interval),this.replayInterval.splice(d,
1))}};C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype.setVolume=function(a){this._ready?this._player.setVolume(a):this._queueCommand("setVolume",a)};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype.loadPlaylist=function(){this._ready?this._player.loadPlaylist(this.videoId):this._queueCommand("loadPlaylist",this.videoId)};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype.setLoop=function(a){this._ready?this._player.setLoop(a):this._queueCommand("setLoop",a)};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype.getVolume=function(){return this._ready&&this._player.getVolume()||0};C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype.mute=function(){this._ready?this._player.mute():this._queueCommand("mute")};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype.unMute=function(){this._ready?this._player.unMute():this._queueCommand("unMute")};C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype.isMuted=function(){return this._ready&&this._player.isMuted()||!1};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype.setSize=function(a,b){this._ready?this._player.setSize(a,b):this._queueCommand("setSize",a,b)};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype.setPlaybackRate=function(a){this._ready?this._player.setPlaybackRate(a):this._queueCommand("setPlaybackRate",a)};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype.setPlaybackQuality=function(a){this._ready?this._player.setPlaybackQuality(a):this._queueCommand("setPlaybackQuality",a)};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype.getPlaybackRate=function(){return this._ready&&this._player.getPlaybackRate()||1};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype.getAvailablePlaybackRates=function(){return this._ready&&this._player.getAvailablePlaybackRates()||[1]};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype.getDuration=function(){return this._ready&&this._player.getDuration()||0};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype.getProgress=function(){return this._ready&&this._player.getVideoLoadedFraction()||0};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype.getState=function(){return this._ready&&YOUTUBE_STATES[this._player.getPlayerState()]||"unstarted"};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype.getCurrentTime=function(){return this._ready&&this._player.getCurrentTime()||0};C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype.destroy=function(){this._destroy()};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype._destroy=function(a){this.destroyed||(this.destroyed=!0,this._player&&(this._player.stopVideo&&this._player.stopVideo(),this._player.destroy()),this._player=this._api=this._opts=this._id=this.videoId=null,this._ready=!1,this._queue=null,this._stopInterval(),this.removeListener("playing",this._startInterval),this.removeListener("paused",
this._stopInterval),this.removeListener("buffering",this._stopInterval),this.removeListener("unstarted",this._stopInterval),this.removeListener("ended",this._stopInterval),a&&this.emit("error",a))};C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype._queueCommand=function(a,b){for(var c=[],d=1;d<arguments.length;++d)c[d-1]=arguments[d];this.destroyed||this._queue.push([a,c])};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype._flushQueue=function(){for(;this._queue.length;){var a=this._queue.shift();this[a[0]].apply(this,a[1])}};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype._loadIframeAPI=function(a){if(window.YT&&"function"===typeof window.YT.Player)return a(null,window.YT);loadIframeAPICallbacks.push(a);Array.from(document.getElementsByTagName("script")).some(function(a){return a.src===YOUTUBE_IFRAME_API_SRC})||loadScript(YOUTUBE_IFRAME_API_SRC).catch(function(a){for(;loadIframeAPICallbacks.length;)loadIframeAPICallbacks.shift()(a)});
var b=window.onYouTubeIframeAPIReady;window.onYouTubeIframeAPIReady=function(){for("function"===typeof b&&b();loadIframeAPICallbacks.length;)loadIframeAPICallbacks.shift()(null,window.YT)}};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype._createPlayer=function(a){var b=this;if(!this.destroyed){var c=this._opts;this._player=new this._api.Player(this._id,{width:c.width,height:c.height,videoId:a,host:c.host,playerVars:{autoplay:c.autoplay?1:0,mute:c.mute?1:0,hl:null!=c.captions&&!1!==c.captions?c.captions:void 0,cc_lang_pref:null!=c.captions&&!1!==c.captions?c.captions:
void 0,controls:c.controls?2:0,enablejsapi:1,allowfullscreen:!0,iv_load_policy:c.annotations?1:3,modestbranding:c.modestBranding?1:0,origin:"*",rel:c.related?1:0,mode:"transparent",showinfo:0,html5:1,version:3,playerapiid:"iframe_YTP_1624972482514"},events:{onReady:function(){return b._onReady(a)},onStateChange:function(a){return b._onStateChange(a)},onPlaybackQualityChange:function(a){return b._onPlaybackQualityChange(a)},onPlaybackRateChange:function(a){return b._onPlaybackRateChange(a)},onError:function(a){return b._onError(a)}}})}};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype._onReady=function(a){this.destroyed||(this._ready=!0,this.load(this.videoId,this._autoplay,this._start),this._flushQueue())};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype._onStateChange=function(a){if(!this.destroyed){var b=YOUTUBE_STATES[a.data];if(b)["paused","buffering","ended"].includes(b)&&this._onTimeupdate(),this.emit(b),["unstarted","playing","cued"].includes(b)&&this._onTimeupdate();else throw Error("Unrecognized state change: "+a);}};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype._onPlaybackQualityChange=function(a){this.destroyed||this.emit("playbackQualityChange",a.data)};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype._onPlaybackRateChange=function(a){this.destroyed||this.emit("playbackRateChange",a.data)};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype._onError=function(a){if(!this.destroyed&&(a=a.data,a!==YOUTUBE_ERROR.HTML5_ERROR)){if(a===YOUTUBE_ERROR.UNPLAYABLE_1||a===YOUTUBE_ERROR.UNPLAYABLE_2||a===YOUTUBE_ERROR.NOT_FOUND||a===YOUTUBE_ERROR.INVALID_PARAM)return this.emit("unplayable",this.videoId);this._destroy(Error("YouTube Player Error. Unknown error code: "+a))}};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype._startOptimizeDisplayEvent=function(){var a=this;this._resizeListener||(this._resizeListener=function(){return a._optimizeDisplayHandler("center, center")},window.addEventListener("resize",this._resizeListener))};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype._onTimeupdate=function(){this.emit("timeupdate",this.getCurrentTime())};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype._startInterval=function(){var a=this;this._interval=setInterval(function(){return a._onTimeupdate()},this._opts.timeupdateFrequency)};
C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0.prototype._stopInterval=function(){clearInterval(this._interval);this._interval=null};YouTubePlayer=C_$hudson$workspace$Mobirise_Windows_release_web$Release$release$win_ia32_unpacked$resources$_app_asar$web$app$themes$startm5$plugins$ytplayer$index$classdecl$var0;

View File

@ -17,8 +17,8 @@
<% } %>
<div class="input-box">
<span class="details">Username</span>
<input type="text" name="usernameOrEmail" placeholder="Enter your email" required>
<span class="details">Username or Email</span>
<input type="text" name="usernameOrEmail" placeholder="Enter your Username or Email" required>
</div>
<div class="button">

View File

@ -20,7 +20,8 @@
<nav>
<a href="/inusers">In-House Users</a>
<a href="#">Users</a>
<a href="#">Sensors</a>
<a href="/sensors">Sensors</a>
<a href="/locations">Locations</a>
<a href="/logout">Logout</a>
</nav>

222
Sean/views/index.ejs Normal file
View File

@ -0,0 +1,222 @@
<!DOCTYPE html>
<html >
<head>
<!-- Site made with Mobirise Website Builder v5.9.13, https://mobirise.com -->
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="generator" content="Mobirise v5.9.13, mobirise.com">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
<meta name="description" content="">
<title>Home</title>
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap-grid.min.css">
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap-reboot.min.css">
<link rel="stylesheet" href="assets/parallax/jarallax.css">
<link rel="stylesheet" href="assets/animatecss/animate.css">
<link rel="stylesheet" href="assets/dropdown/css/style.css">
<link rel="stylesheet" href="assets/socicon/css/styles.css">
<link rel="stylesheet" href="assets/theme/css/style.css">
<link rel="preload" href="https://fonts.googleapis.com/css?family=Inter+Tight:100,200,300,400,500,600,700,800,900,100i,200i,300i,400i,500i,600i,700i,800i,900i&display=swap" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter+Tight:100,200,300,400,500,600,700,800,900,100i,200i,300i,400i,500i,600i,700i,800i,900i&display=swap"></noscript>
<link rel="preload" as="style" href="assets/mobirise/css/mbr-additional.css?v=b1g2Yh"><link rel="stylesheet" href="assets/mobirise/css/mbr-additional.css?v=b1g2Yh" type="text/css">
</head>
<body>
<section data-bs-version="5.1" class="menu menu5 cid-u2mrL2wdLO" once="menu" id="menu05-1i">
<nav class="navbar navbar-dropdown navbar-fixed-top navbar-expand-lg">
<div class="container">
<div class="navbar-brand">
<span class="navbar-caption-wrap"><a class="navbar-caption text-black display-4" href="https://mobiri.se">EcoSaver Management</a></span>
</div>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-bs-toggle="collapse" data-target="#navbarSupportedContent" data-bs-target="#navbarSupportedContent" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<div class="hamburger">
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav nav-dropdown" data-app-modern-menu="true">
<li class="nav-item">
<a class="nav-link link text-black display-4" href="index.html#header01-7">About</a>
</li>
<li class="nav-item">
<a class="nav-link link text-black display-4" href="index.html#features04-w" aria-expanded="false">Services</a>
</li>
<li class="nav-item">
<a class="nav-link link text-black display-4" href="index.html#contacts02-9">Contacts</a>
</li>
</ul>
<div class="navbar-buttons mbr-section-btn"><a class="btn btn-primary display-4" href="https://mobiri.se">Login</a></div>
</div>
</div>
</nav>
</section>
<section data-bs-version="5.1" class="header5 cid-tJS6uM4N87" id="header05-1">
<div class="topbg"></div>
<div class="align-center container">
<div class="row justify-content-center">
<div class="col-md-12 col-lg-9">
<h1 class="mbr-section-title mbr-fonts-style mb-4 display-1"><strong>WELCOME TO ECOSAVER MANAGEMENT</strong></h1>
<p class="mbr-text mbr-fonts-style mb-4 display-7">Here at ECOSAVER MANAGEMENT, we are tasked to secure and protect ECOSAVER to provide<br>consumers with the best experience</p>
</div>
</div>
<div class="row mt-5 justify-content-center">
<div class="col-12 col-lg-12">
<img src="assets/images/ecosostenibilita-1000x500.jpeg" alt="Mobirise Website Builder" title="">
</div>
</div>
</div>
</section>
<section data-bs-version="5.1" class="features4 cid-tMlEXTHLbS" id="features04-w">
<div class="container-fluid">
<div class="row justify-content-center">
<div class="col-12 content-head">
<div class="mbr-section-head mb-5">
<h4 class="mbr-section-title mbr-fonts-style align-center mb-0 display-2"><strong>Our Services</strong></h4>
<h5 class="mbr-section-subtitle mbr-fonts-style align-center mb-0 mt-4 display-7">At EcoSaver, we are committed to empowering individuals and businesses to embrace a greener, more sustainable lifestyle. We are designed to make eco-friendly choices accessible and effortless, ensuring a positive impact on both the environment and your bottom line.</h5>
</div>
</div>
</div>
<div class="row">
<div class="item features-image col-12 col-md-6 col-lg-6 active">
<div class="item-wrapper">
<div class="item-img">
<img src="assets/images/air-pollution-1679x944.jpg" alt="Mobirise Website Builder" data-slide-to="1" data-bs-slide-to="1">
</div>
<div class="item-content">
<h5 class="item-title mbr-fonts-style display-5"><strong>Ecosaver</strong></h5>
<p class="mbr-text mbr-fonts-style display-7">EcoSaver is proud to offer a cutting-edge Air Quality Service, providing real-time air quality data for Singapore. It allows anyone to access crucial information about the air they breathe, empowering individuals and communities to make informed decisions for their well-being.</p>
<div class="mbr-section-btn item-footer"><a href="" class="btn item-btn btn-primary display-7">Learn more</a></div>
</div>
</div>
</div><div class="item features-image col-12 col-md-6 col-lg-6">
<div class="item-wrapper">
<div class="item-img">
<img src="assets/images/the-expansion-of-cloud-applications-has-added-to...-766x476.png" alt="Mobirise Website Builder" data-slide-to="0" data-bs-slide-to="0">
</div>
<div class="item-content">
<h5 class="item-title mbr-fonts-style display-5"><strong>Ecosaver Management</strong></h5>
<p class="mbr-text mbr-fonts-style display-7">
EcoSaver Management is unwaveringly dedicated to delivering paramount security within the EcoSaver system. Our paramount objective is to establish and maintain a secure environment, ensuring the safety of every individual who engages with the EcoSaver platform.</p>
<div class="mbr-section-btn item-footer"><a href="" class="btn item-btn btn-primary display-7">Learn more</a></div>
</div>
</div>
</div>
</div>
</div>
</section>
<section data-bs-version="5.1" class="header1 cid-tJS9vXDdRK" id="header01-7">
<div class="container">
<div class="row justify-content-center">
<div class="col-12 col-md-12 col-lg-7 image-wrapper">
<img class="w-100" src="assets/images/gallery01.jpg" alt="Mobirise Website Builder">
</div>
<div class="col-12 col-lg col-md-12">
<div class="text-wrapper align-left">
<h1 class="mbr-section-title mbr-fonts-style mb-4 display-2"><strong>About us</strong></h1>
<p class="mbr-text mbr-fonts-style mb-4 display-7">
EcoSaver is made by 4 student of Temasek Polytechnic studying cyber security and digital forensics for their major project</p>
</div>
</div>
</div>
</div>
</section>
<section data-bs-version="5.1" class="contacts2 map1 cid-tLdYHD757A mbr-parallax-background" id="contacts02-9">
<div class="mbr-overlay" style="opacity: 0.5; background-color: rgb(0, 0, 0);"></div>
<div class="container-fluid">
<div class="mbr-section-head mb-5">
<h3 class="mbr-section-title mbr-fonts-style align-center mb-0 display-2">
<strong>Contacts</strong>
</h3>
</div>
<div class="row justify-content-center mt-4">
<div class="card col-12 col-md-5">
<div class="card-wrapper">
<div class="text-wrapper">
<h5 class="cardTitle mbr-fonts-style mb-2 display-5">
<strong>Get in touch</strong></h5>
<ul class="list mbr-fonts-style display-7">
<li class="mbr-text item-wrap"><span style="font-size: 1.4rem;">Phone: 6666 6666</span></li>
<li class="mbr-text item-wrap">Email: ecosaverx@gmail.com</li><li class="mbr-text item-wrap"><br></li>
<li class="mbr-text item-wrap">Address: </li><li class="mbr-text item-wrap">21 Tampines Ave 1, Singapore 529757</li><li class="mbr-text item-wrap"><span style="font-size: 1.4rem;">Working hours:</span><br></li><li class="mbr-text item-wrap">8:30AM - 6:00PM</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</section>
<section data-bs-version="5.1" class="footer1 programm5 cid-tJS9NNcTLZ" once="footers" id="footer03-8">
<div class="container">
<div class="row">
<div class="row-links mb-4">
<ul class="header-menu">
<li class="header-menu-item mbr-fonts-style display-5"><a href="index.html#header01-7" class="text-danger">About</a></li><li class="header-menu-item mbr-fonts-style display-5"><a href="index.html#features04-w" class="text-danger text-primary">Services</a></li><li class="header-menu-item mbr-fonts-style display-5"><a href="index.html#contacts02-9" class="text-danger">Contacts</a></li></ul>
</div>
<div class="col-12">
</div>
<div class="col-12 mt-5">
<p class="mbr-fonts-style copyright display-7">
© Copyright 2030 Ecosaver - All Rights Reserved
</p>
</div>
</div>
</div>
</section><section class="display-7" style="padding: 0;align-items: center;justify-content: center;flex-wrap: wrap; align-content: center;display: flex;position: relative;height: 4rem;"><a href="https://mobiri.se/3136804" style="flex: 1 1;height: 4rem;position: absolute;width: 100%;z-index: 1;"><img alt="" style="height: 4rem;" src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="></a><p style="margin: 0;text-align: center;" class="display-7">&#8204;</p><a style="z-index:1" href="https://mobirise.com/builder/ai-website-builder.html">AI Website Builder</a></section><script src="assets/bootstrap/js/bootstrap.bundle.min.js"></script> <script src="assets/parallax/jarallax.js"></script> <script src="assets/smoothscroll/smooth-scroll.js"></script> <script src="assets/ytplayer/index.js"></script> <script src="assets/dropdown/js/navbar-dropdown.js"></script> <script src="assets/theme/js/script.js"></script>
<input name="animation" type="hidden">
</body>
</html>

View File

@ -173,21 +173,19 @@
</div>
<script nonce="<%= nonce %>">
<script >
const allUsers = <%- JSON.stringify(allUsers) %>;
const currentUsername = '<%= currentUsername %>';
const nonce = "<%= nonce %>"
console.log('Nonce:', nonce);
</script>
<script src="https://code.jquery.com/jquery-3.6.4.min.js" nonce="<%= nonce %>"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/5.3.0/js/bootstrap.bundle.min.js" nonce="<%= nonce %>" ></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.17.4/xlsx.full.min.js" nonce="<%= nonce %>"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js" nonce="<%= nonce %>"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/exceljs/4.2.1/exceljs.min.js" nonce="<%= nonce %>"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" nonce="<%= nonce %>"></script>
<script src="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.js" nonce="<%= nonce %>"></script>
<script src="inusers.js" nonce="<%= nonce %>"></script>
<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/5.3.0/js/bootstrap.bundle.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.17.4/xlsx.full.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/exceljs/4.2.1/exceljs.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.js"></script>
<script src="inusers.js"></script>
</body>

180
Sean/views/location.js Normal file
View File

@ -0,0 +1,180 @@
$(document).ready(function () {
$('#allLocationLink').on('click', function () {
$('#locationContainer').show();
$('#createLocationForm').hide();
$('#updateLocationForm').hide();
$('#deleteLocationForm').hide();
});
$('#addLocationLink').on('click', function () {
$('#locationContainer').hide();
$('#createLocationForm').show();
$('#updateLocationForm').hide();
$('#deleteLocationForm').hide();
});
$('#updateLocationLink').on('click', function () {
$('#locationContainer').hide();
$('#createLocationForm').hide();
$('#updateLocationForm').show();
$('#deleteLocationForm').hide();
});
$('#deleteLocationLink').on('click', function () {
$('#locationContainer').hide();
$('#createLocationForm').hide();
$('#updateLocationForm').show();
$('#deleteLocationForm').show();
});
});
let locationArray = [];
function populateTableAndArray(data) {
const tableBody = document.getElementById("locationTableBody");
// Clear existing rows and array
tableBody.innerHTML = "";
locationArray.length = 0;
// Loop through the data and create table rows
data.forEach(location => {
const row = document.createElement("tr");
row.innerHTML = `
<td>${location.id}</td>
<td>${location.location}</td>
<td>${location.description}</td>
`;
tableBody.appendChild(row);
// Push location data to the array
locationArray.push(location);
});
}
populateTableAndArray(locationsData);
populateLocationDropdown();
$('#locationForm').on('submit', function (e) {
e.preventDefault();
const location= $('#location').val();
const user = req.session.jobTitle
const description= $('#description').val();
const csrf_token = $('#userForm input[name="csrf_token"]').val();
fetch('/location/new', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: location,
added_by: user,
description: description,
csrf_token: csrf_token
}),
})
.then(response => {
if (response.ok) {
// Status 201 indicates successful creation
return response.json();
} else {
return response.json().then(data => {
throw new Error(data.message || `HTTP error! Status: ${response.status}`);
});
}
})
.then(data => {
console.log(`Location added successfully. Message: ${data.message}`);
alert('Location added successfully!');
resetFormFields();
})
.catch(error => {
console.error('Location not added successfully', error);
// Handle error as needed
});
});
function populateLocationDropdown() {
// Clear existing options
$('#locationDropdown').empty();
// Populate the dropdown with options from locationArray
locationArray.forEach(location => {
$('#locationDropdown').append(`<option value="${location.id}">${location.name}</option>`);
});
}
$('#updateForm').on('submit', function (e) {
e.preventDefault();
const selectedLocationId = $('#locationDropdown').val();
const location= $('#location').val();
const user = req.session.jobTitle
const description=$('#description').val();
const csrf_token = $('#userForm input[name="csrf_token"]').val();
fetch('/location/update', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
id:selectedLocationId,
name: location,
added_by: user,
description: description,
csrf_token: csrf_token
}),
})
.then(response => {
if (response.ok) {
// Status 201 indicates successful creation
return response.json();
} else {
return response.json().then(data => {
throw new Error(data.message || `HTTP error! Status: ${response.status}`);
});
}
})
.then(data => {
console.log(`Location uppdated successfully. Message: ${data.message}`);
alert('Location updated successfully!');
resetFormFields();
})
.catch(error => {
console.error('Location not updated successfully', error);
// Handle error as needed
});
});
$('#deleteForm').on('submit', function (e) {
e.preventDefault();
const selectedLocationId = $('#locationDropdown').val();
const csrf_token = $('#userForm input[name="csrf_token"]').val();
fetch('/location/delete', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
id:selectedLocationId,
csrf_token: csrf_token
}),
})
.then(response => {
if (response.ok) {
// Status 201 indicates successful creation
return response.json();
} else {
return response.json().then(data => {
throw new Error(data.message || `HTTP error! Status: ${response.status}`);
});
}
})
.then(data => {
console.log(`Location deleted successfully. Message: ${data.message}`);
alert('Location deleted successfully!');
resetFormFields();
})
.catch(error => {
console.error('Location not deleted successfully', error);
// Handle error as needed
});
});

120
Sean/views/locations.ejs Normal file
View File

@ -0,0 +1,120 @@
<!-- views/location.ejs -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Home</title>
<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;700&display=swap">
</head>
<body>
<header>
<h1>ECOSAVER MANAGEMENT</h1>
</header>
<nav>
<a href="#" id="allLocationLink">All Locations</a>
<a href="#" id="addLocationLink">Add Locations</a>
<a href="#" id="updateLocationLink">Update Locations</a>
<a href="#">Delete Locations</a>
<a href="/home" id="homeLink">Home</a>
</nav>
<main>
<h2>Welcome to the Location Page</h2>
</main>
<div id="locationContainer">
<table class="nice-table">
<thead>
<tr>
<th>ID</th>
<th>Location</th>
<th>Descriptions</th>
</tr>
</thead>
<tbody id="locationTableBody"></tbody>
</table>
</div>
<div id="createLocationForm" class="location-creation-container custom-location-form" style="display: none;">
<h3>Add Location</h3>
<div class="content">
<form action="/location/new" id="locationForm" method="post">
<div class="Location-details">
<div class="input-box">
<span class="details">Location Name</span>
<input type="text" name="location" id="location" placeholder="Enter Location name" required>
</div>
<div class="input-box">
<span class="details">Description</span>
<input type="text" name="description" id="description" placeholder="Enter the description here" required>
</div>
</div>
<input type="hidden" name="csrf_token" value="<%= csrfToken %>">
<div class="button">
<input type="submit" value="submit">
</div>
</form>
</div>
</div>
<div id="updateLocationForm" class="location-update-container" style="display: none;">
<h3>Update Location</h3>
<div class="content">
<form action="/location/update" id="updateForm" method="put">
<div class="Location-details">
<div class="input-box">
<span class="details">Location to Update</span>
<select name="location" id="locationDropdown" required>
</select>
</div>
<div class="input-box">
<span class="details">Location Name</span>
<input type="text" name="location" id="location" placeholder="Enter Location name" required>
</div>
<div class="input-box">
<span class="details">Description</span>
<input type="text" name="description" id="description" placeholder="Enter the description here" required>
</div>
</div>
<input type="hidden" name="csrf_token" value="<%= csrfToken %>">
<div class="button">
<input type="submit" value="submit">
</div>
</form>
</div>
</div>
<div id="deleteLocationForm" class="location-delete-container" style="display: none;">
<h3>Delete Location</h3>
<div class="content">
<form action="/location/delete" id="deleteForm" method="delete">
<div class="Location-details">
<div class="input-box">
<span class="details">Location to Delete</span>
<select name="location" id="locationDropdown" required>
</select>
</div>
</div>
<input type="hidden" name="csrf_token" value="<%= csrfToken %>">
<div class="button">
<input type="submit" value="submit">
</div>
</form>
</div>
</div>
<footer>
Any Issue faced, Please contact the administrator at 11111111 or ecosaverAdmin@gmail.com
</footer>
<script>
const locationsData = <%- JSON.stringify(locationsData) %>;
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/2.3.3/purify.min.js"></script>
<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
<script src="location.js"></script>
</body>
</html>

225
Sean/views/sensor.js Normal file
View File

@ -0,0 +1,225 @@
$(document).ready(function () {
$('#allSensorLink').on('click', function () {
$('#sensorContainer').show();
$('#createSensorForm').hide();
$('#additional-text4').hide();
$('#updateSensorForm').hide();
$('#deleteSensorForm').hide();
});
$('#addSensorLink').on('click', function () {
$('#sensorContainer').hide();
$('#createSensorForm').show();
$('#additional-text4').show();
$('#updateSensorForm').hide();
$('#deleteSensorForm').hide();
});
$('#updateSensorLink').on('click', function () {
$('#sensorContainer').hide();
$('#createSensorForm').show();
$('#additional-text4').show();
$('#updateSensorForm').show();
$('#deleteSensorForm').hide();
});
$('#deleteSensorLink').on('click', function () {
$('#sensorContainer').hide();
$('#createSensorForm').show();
$('#additional-text4').show();
$('#updateSensorForm').hide();
$('#deleteSensorForm').show();
});
});
function populateTableAndArray(data, locationsArray) {
const tableBody = document.getElementById("sensorTableBody");
// Clear existing rows and array
tableBody.innerHTML = "";
sensorArray.length = 0;
// Loop through the data and create table rows
data.forEach(sensor => {
const location = locationsArray.find(loc => loc.id === sensor.location);
const row = document.createElement("tr");
row.innerHTML = `
<td>${sensor.id}</td>
<td>${sensor.sensorname}</td>
<td>${sensor.added_by}</td>
<td>${sensor.description}</td>
<td>${location ? location.name : 'Unknown Location'}</td>
`;
tableBody.appendChild(row);
// Push sensor data to the array
sensorArray.push(sensor);
});
}
// Assuming locationsArray is defined elsewhere in your code
populateTableAndArray(sensorData);
console.log(sensorArray);
function populateLocationDropdown() {
const locationDropdown = document.getElementById('locationDropdown');
// Clear existing options
locationDropdown.innerHTML = '';
// Add a default option
const defaultOption = document.createElement('option');
defaultOption.text = 'Select a Location';
defaultOption.value = '';
locationDropdown.add(defaultOption);
// Add locations as options
locationsArray.forEach(location => {
const option = document.createElement('option');
option.text = location.location;
option.value = location.id;
locationDropdown.add(option);
});
}
populateLocationDropdown();
function populateSensorDropdown() {
const sensorDropdown = document.getElementById('sensorDropdown');
// Clear existing options
sensorDropdown.innerHTML = '';
// Add a default option
const defaultOption = document.createElement('option');
defaultOption.text = 'Select a Sensor';
defaultOption.value = '';
sensorDropdown.add(defaultOption);
// Add locations as options
sensorArray.forEach(location => {
const option = document.createElement('option');
option.text = sensor.sensorname;
option.value = sensor.id;
sensorDropdown.add(option);
});
}
populateSensorDropdown();
$('#sensorForm').on('submit', function (e) {
e.preventDefault();
const sensor = $('#sensor').val();
const user = req.session.jobTitle;
const macAddress = $('#macAddress').val();
const description = $('#description').val();
const location = $('#location').val();
const csrf_token = $('#userForm input[name="csrf_token"]').val();
fetch('sensor/new', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
sensorname: sensor,
added_by: user,
mac_address: macAddress,
description: description,
location: location,
csrf_token: csrf_token
}),
})
.then(response => {
if (response.ok) {
// Status 201 indicates successful creation
return response.json();
} else {
return response.json().then(data => {
throw new Error(data.message || `HTTP error! Status: ${response.status}`);
});
}
})
.then(data => {
console.log(`Sensor added successfully. Message: ${data.message}`);
alert('Sensor added successfully!');
resetFormFields();
})
.catch(error => {
console.error('Location not added successfully', error);
// Handle error as needed
});
});
$('#updatesensorForm').on('submit', function (e) {
e.preventDefault();
const id = $('#sensorDropdown').val();
const sensorname = $('#sensorname').val();
const user = req.session.jobTitle;
const macAddress = $('#macAddress').val();
const description = $('#description').val();
const location = $('#locationDropdown').val();
const csrf_token = $('#userForm input[name="csrf_token"]').val();
fetch('sensor/update', {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
id: id,
sensorname: sensorname,
added_by: user,
mac_address: macAddress,
description: description,
location: location,
csrf_token: csrf_token
}),
})
.then(response => {
if (response.ok) {
// Status 201 indicates successful creation
return response.json();
} else {
return response.json().then(data => {
throw new Error(data.message || `HTTP error! Status: ${response.status}`);
});
}
})
.then(data => {
console.log(`Sensor updated successfully. Message: ${data.message}`);
alert('Sensor updated successfully!');
resetFormFields();
})
.catch(error => {
console.error('Sensor not updated successfully', error);
// Handle error as needed
});
});
$('#deleteForm').on('submit', function (e) {
e.preventDefault();
const selectedSensorId = $('#sensorDropdown').val();
const csrf_token = $('#userForm input[name="csrf_token"]').val();
fetch('/sensor/delete', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
id:selectedSensorId,
csrf_token: csrf_token
}),
})
.then(response => {
if (response.ok) {
// Status 201 indicates successful creation
return response.json();
} else {
return response.json().then(data => {
throw new Error(data.message || `HTTP error! Status: ${response.status}`);
});
}
})
.then(data => {
console.log(`Sensor deleted successfully. Message: ${data.message}`);
alert('Sensor deleted successfully!');
resetFormFields();
})
.catch(error => {
console.error('Sensor not deleted successfully', error);
// Handle error as needed
});
});

153
Sean/views/sensors.ejs Normal file
View File

@ -0,0 +1,153 @@
<!-- views/sensor.ejs -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Home</title>
<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;700&display=swap">
</head>
<body>
<header>
<h1>ECOSAVER MANAGEMENT</h1>
</header>
<nav>
<a href="#" id="allSensorLink">All Sensor</a>
<a href="#" id="addSensorLink">Add Sensor</a>
<a href="#" id="updateSensorLink">Update Sensor</a>
<a href="#" id="deleteSensorLink">Delete Sensor</a>
<a href="/home" id="homeLink">Home</a>
</nav>
<main>
<h2>Welcome to the Sensor Page</h2>
</main>
<div id="sensorContainer">
<table class="nice-table">
<thead>
<tr>
<th>ID</th>
<th>Sensor Name</th>
<th>Added By</th>
<th>Mac Address</th>
<th>Description</th>
<th>Location</th>
</tr>
</thead>
<tbody id="sensorTableBody"></tbody>
</table>
</div>
<div id="createSensorForm" class="sensor-creation-container" style="display: none;">
<h3>Add Sensor</h3>
<div class="content">
<form action="/api/v0/sensor/new" id="sensorForm" method="post">
<div class="Sensor-details">
<div class="input-box">
<span class="details">Sensor Name</span>
<input type="text" name="sensor" id="sensor" placeholder="Enter Sensor name" required>
</div>
<div class="input-box">
<span class="details">Mac Address</span>
<input type="text" name="macAddress" id="macAddress" placeholder="Enter the Mac Address" required>
</div>
<div class="input-box">
<span class="details">Description</span>
<input type="text" name="description" id="description" placeholder="Enter the description here" required>
</div>
<div class="input-box">
<span class="details">Location</span>
<select name="location" id="locationDropdown" required>
</select>
</div>
</div>
<input type="hidden" name="csrf_token" value="<%= csrfToken %>">
<div class="button">
<input type="submit" value="Submit">
</div>
</form>
</div>
</div>
<div id="additional-text4" style="display: none;">
<div class="condition">
<span>Conditions for creating a Sensor:</span>
<ul>
<li class="error">Please Remember to fill all inputs.</li>
<li class="error">Please ensure all inputs are correct before adding</li>
</ul>
</div>
</div>
<div id="updateSensorForm" class="sensor-update-container" style="display: none;">
<h3>Add Location</h3>
<div class="content">
<form action="/sensor/update" id="updateForm" method="put">
<div class="Location-details">
<div class="input-box">
<span class="details">Sensor to Update</span>
<select name="Sensor" id="sensorDropdown" required>
</select>
</div>
<div class="input-box">
<span class="details">Sensor Name</span>
<input type="text" name="sensorname" id="sensorname" placeholder="Enter Sensor name" required>
</div>
<div class="input-box">
<span class="details">Mac Address</span>
<input type="text" name="macAddress" id="macAddress" placeholder="Enter the Mac Address" required>
</div>
<div class="input-box">
<span class="details">Description</span>
<input type="text" name="description" id="description" placeholder="Enter the description here" required>
</div>
</div>
<div class="input-box">
<span class="details">Location</span>
<select name="location" id="locationDropdown" required>
</select>
</div>
</div>
<input type="hidden" name="csrf_token" value="<%= csrfToken %>">
<input type="hidden" name="csrf_token" value="<%= csrfToken %>">
<div class="button">
<input type="submit" value="submit">
</div>
</form>
</div>
</div>
<div id="deleteSensorForm" class="sensor-delete-container" style="display: none;">
<h3>Delete Location</h3>
<div class="content">
<form action="/sensor/delete" id="deleteForm" method="delete">
<div class="Location-details">
<div class="input-box">
<span class="details">Sensor to Delete</span>
<select name="sensor" id="sensorDropdown" required>
</select>
</div>
</div>
<input type="hidden" name="csrf_token" value="<%= csrfToken %>">
<div class="button">
<input type="submit" value="submit">
</div>
</form>
</div>
</div>
<footer>
Any Issue faced, Please contact the administrator at 11111111 or ecosaverAdmin@gmail.com
</footer>
</body>
<script>
const locationsArray = <%-JSON.stringify(locationsData) %>;
const sensorArray = <%- JSON.stringify(sensorData) %>;
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/2.3.3/purify.min.js"></script>
<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
<script src="sensor.js"></script>
</html>

View File

@ -556,4 +556,83 @@ footer {
font-weight: bold; /* Make condition labels bold */
}
.sensor-creation-container {
max-width: 600px;
margin: 10px auto;
background-color: #f9f9f9;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
margin-left: 20px; /* Adjust the value as needed */
}
.input-box {
margin-bottom: 15px;
}
.input-box input,
.input-box select {
width: 100%;
padding: 8px;
box-sizing: border-box;
}
.button input {
background-color: #4CAF50;
color: white;
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
}
.button input:hover {
background-color: #45a049;
}
#additional-text4 {
width: 30%; /* Adjust the width as needed */
margin-left: 500px; /* Push it to the right */
margin-top: -450px; /* Adjust the negative margin to move it up */
padding: 10px; /* Add padding for spacing */
background-color: #f4f4f4; /* Add background color if desired */
border-radius: 5px; /* Add border-radius for rounded corners */
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); /* Add box shadow for a subtle effect */
border: 1px solid #ddd; /* Add a 1px solid border with light gray color */
font-family: 'Open Sans', sans-serif;
}
#additional-text4 p {
font-size: 16px; /* Adjust font size as needed */
line-height: 1.6; /* Adjust line height for better readability */
}
#additional-text4 .condition {
margin-bottom: 10px; /* Add space between conditions */
}
#additional-text4 .condition span {
font-weight: bold; /* Make condition labels bold */
}
#additional-text4 .condition.error {
color: red; /* Change text color for error conditions */
}
#additional-text4 .condition.success {
color: green; /* Change text color for success conditions */
}
.custom-location-form {
max-width: 600px;
margin: 10px auto 20px; /* Adjust the top and bottom margins */
background-color: #f9f9f9;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
margin-left: 20px; /* Adjust the left margin as needed */
margin-top: 0px; /* Adjust the top margin to move it up */
}
/* Add any other specific styles for this form */
.custom-location-form h3 {
color: #333; /* Customize the heading color */
}

4
api.MD
View File

@ -169,9 +169,7 @@ http://localhost/api/v0/sensor-data/filter?windspeed=highest&limit=1
http://localhost/api/v0/sensor-data/data?week=1&sensorid=1&locationid=1&page=2&pagesize=10
curl localhost/api/v0/user/register -H "Content-Type: application/json" -X POST -d '{"username": "testuser123", "password": "thisisthesystemuserpasswordnoob", "email": "testuser123@ecosaver.com", "address": "Nanyang Polytechnic 180 Ang Mo Kio Avenue 8 Singapore 569830", "phone": "12345678"}'
curl localhost:3000/api/v0/apikey/new -H "Content-Type: application/json" -X POST -d '{"userid": "5", "permission": "canRead"}'
curl localhost:3000/api/v0/token/new -H "Content-Type: application/json" -X POST -d '{"userid": "5", "permission": "canRead"}'

View File

@ -7,6 +7,8 @@ const ejs = require("ejs");
module.exports = app;
process.nextTick(() => require("./mqttApp"));
app.use(express.json());
app.set("json spaces", 2);
@ -14,12 +16,16 @@ app.set("json spaces", 2);
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
limit: 600, // Limit each IP to 100 requests per `window` (here, per 15 minutes).
standardHeaders: 'draft-7', // draft-6: `RateLimit-*` headers; draft-7: combined `RateLimit` header
standardHeaders: "draft-7", // draft-6: `RateLimit-*` headers; draft-7: combined `RateLimit` header
legacyHeaders: false, // Disable the `X-RateLimit-*` headers.
});
// Hold list of functions to run when the server is ready
app.onListen = [function(){console.log('Express is ready')}];
app.onListen = [
function () {
console.log("Express is ready");
},
];
// Apply the rate limiting middleware to all requests.
app.use(limiter);
@ -34,10 +40,9 @@ app.set("view engine", "ejs");
// Have express server static content( images, CSS, browser JS) from the public
app.use(express.static(path.join(__dirname, "./public")));
//route logic
app.use("/api/seed/v0" ,require("./routes/seed_route.js"));
app.use("/api/v0", require("./routes/api_routes"));
app.use("/api/seed/v0", require("./routes/seed_route.js"));
app.use("/api/v0", require("./routes/api_routes"));
//render logic
app.use("/", require("./routes/render"));
@ -45,19 +50,17 @@ app.use("/", require("./routes/render"));
// Catch 404 and forward to error handler. If none of the above routes are
// used, this is what will be called.
app.use(function (req, res, next) {
if (req.is("application/json")) {
var err = new Error("Not Found");
err.message = "Page not found";
err.status = 404;
next(err);
}
else{
//application/json; charset=utf-8
if (req.is("application/json" || "application/json; charset=utf-8")) {
var err = new Error("Not Found");
err.message = "Page not found";
err.status = 404;
next(err);
} else {
res.status(404).render("404");
}
});
// Error handler. This is where `next()` will go on error
app.use(function (err, req, res, next) {
console.error(err.status || res.status, err.name, req.method, req.url);
@ -76,14 +79,20 @@ app.use(function (err, req, res, next) {
keyErrors[item.path] = item.message;
}
}
res.status = 422;
}
if (![404, 422].includes(err.status || res.status)) {
console.error(err.message);
console.error(err.stack);
console.error("=========================================");
}
res.status(err.status || 500);
console.log(keyErrors);
res.json({
name: err.name,
name: err.name || "Unknown error",
message: err.message,
keyErrors,
});
});

View File

@ -5,7 +5,6 @@
*/
const app = require('../app');
const mqttApp = require('../mqttApp');
const debug = require('debug')('proxy-api:server');
const http = require('http');
const path = require('path');

View File

@ -4,8 +4,8 @@ const { sequelize } = require("../mySQL");
const { userModel } = require("./userModel");
//sequelize.sync();
const apikeyModel = sequelize.define(
"apikey",
const tokenModel = sequelize.define(
"token",
{
id: {
type: DataTypes.INTEGER,
@ -28,7 +28,7 @@ const apikeyModel = sequelize.define(
key: "id",
},
},
apikey: {
token: {
type: DataTypes.STRING,
allowNull: false,
length: 255,
@ -45,7 +45,14 @@ const apikeyModel = sequelize.define(
validate: {
notEmpty: true,
len: [1, 255],
isIn: [["canRead", "canWrite" , "auto-generated"]],
isIn: [["canRead", "canWrite",]],
},
},
expiration: {
type: DataTypes.DATE,
allowNull: false,
validate: {
isDate: true,
},
},
createdAt: {
@ -61,9 +68,9 @@ const apikeyModel = sequelize.define(
timestamps: true,
}
);
apikeyModel.belongsTo(userModel);
tokenModel.belongsTo(userModel);
module.exports = { apikeyModel };
module.exports = { tokenModel };
/*
class AuthToken extends Model {

View File

@ -1,5 +1,5 @@
const { hash, compareHash } = require("./bcrypt.js");
const { apikeyModel } = require("../database/model/apiKeyModel");
const { tokenModel } = require("../database/model/tokenModel.js");
const { generateUUID } = require("./generateUUID.js");
/*
@ -11,37 +11,38 @@ const { generateUUID } = require("./generateUUID.js");
6) store in database
*/
//can be used for api key or token. Both are the same logic
async function addAPIKey(userId, permission) {
let hashtoken = await generateUUID();
let apikey = await hash(hashtoken);
async function addToken(userId, permission , expiry) {
let uuid = await generateUUID();
let hashtoken = await hash(uuid);
let token = await apikeyModel.create({
let token = await tokenModel.create({
userid: userId,
apikey: apikey,
token: hashtoken,
permission: permission,
expiration: expiry,
});
//user token with - tokenid is table id
return token.id + "-" + hashtoken;
return token.id + "-" + uuid;
}
async function checkAPikey(SuppliedKey, rowid) {
async function checkToken(Supplied, rowid) {
try {
const retrivedKey = await apikeyModel.findOne({
const retrivedToken = await tokenModel.findOne({
raw: true,
attributes: ["apikey", "permission"],
attributes: ["token", "permission"],
where: {
id: rowid,
},
});
//console.log(retrivedKey.apikey);
if (compareHash(SuppliedKey, retrivedKey.apikey)) {
if (compareHash(Supplied, retrivedToken.token)) {
//return true;
return retrivedKey.permission;
return retrivedToken.permission;
}
} catch (error) {
console.error(error);
}
}
module.exports = { addAPIKey , checkAPikey };
module.exports = { addToken , checkToken };

View File

@ -0,0 +1,20 @@
const moment = require("moment");
const currentTime = moment().format("YYYY-MM-DD HH:mm:ss");
//time is taken from the token
function isValid(time){
const timeDiff = moment(currentTime).diff(time, "minutes");
if (timeDiff > 1) {
console.log(timeDiff);
return false;
}
return true;
}
module.exports = { isValid };

View File

@ -6,7 +6,6 @@ async function getLocation() {
}
async function addLocation(name, added_by, description) {
console.log(name, added_by, description);
const location = await locationModel.create({
name: name,
added_by: added_by,
@ -30,7 +29,6 @@ async function updateLocation(id, name, added_by, description) {
}
async function deleteLocation(id) {
//delete by id
const location = await locationModel.destroy({
where: {
id: id,
@ -38,6 +36,7 @@ async function deleteLocation(id) {
});
}
async function getLocationById(id) {
const location = await locationModel.findAll({
where: {

View File

@ -1,5 +1,4 @@
const {sensorModel} = require("../database/model/sensorModel");
const { sensorModel } = require("../database/model/sensorModel");
async function getSensor() {
const sensor = await sensorModel.findAll();
@ -49,12 +48,12 @@ async function updateSensor(
async function deleteSensor(id) {
//delete by id
const sensor = await sensorModel.destroy({
//cascade delete
onDelete: "cascade",
where: {
id: id,
},
});
console.error(error);
}
async function getSensorById(id) {
@ -72,4 +71,4 @@ module.exports = {
updateSensor,
deleteSensor,
getSensorById,
};
};

View File

@ -25,7 +25,7 @@ async function addSensorData(id_sensor, id_location, sensordata) {
locationid: id_location,
measurement: sensordata,
});
io().emit('sensordata:new', sensorData)
io().emit('sensorData:new', sensorData)
return sensorData;
}

View File

@ -1,13 +1,14 @@
const { Op } = require('sequelize')
const { hash, compareHash } = require("./bcrypt.js");
const { addAPIKey } = require("./api");
const { addToken } = require("./api");
const { userModel } = require("../database/model/userModel");
moment = require('moment')
//getuser
//api/v0/user/me
async function getUserID(userid) {
async function getUserByID(userid) {
//console.log(userid);
//console.log(userid.id);
let userRes = await userModel.findByPk(userid.id, {
@ -20,28 +21,6 @@ async function getUserID(userid) {
return userRes;
}
//register
//api/v0/auth/register
async function addUser(user) {
//hash password
let hashed = await hash(user.password);
const addRes = await userModel.create({
firstname: user.firstname,
lastname: user.lastname,
username: user.username,
password: hashed,
email: user.email,
address: user.address,
phone: user.phone,
});
if (addRes) {
return true;
} else {
return false;
}
}
//api/v0/auth/register
/* Registering new user
1) req.body is taken from html form or wtv
@ -92,9 +71,9 @@ async function loginUser(user) {
if (!match) return false;
//console.log('loginUser', userRes.id, userRes.username);
//generate token
let token = await addAPIKey(userRes.id, "auto-generated");
//generate token and permission and experiation time
const currentTime = moment().format('YYYY-MM-DD HH:mm:ss');
let token = await addToken(userRes.id , "canRead" , currentTime);
return { token: token, userid: userRes.id, username: userRes.username };
}
@ -152,7 +131,7 @@ async function updateProfile(user, body) {
}
module.exports = {
getUserID,
getUserByID,
addUser,
loginUser,
updateProfile,

View File

@ -1,65 +0,0 @@
const { checkAPikey } = require("../functions/api.js");
async function apikeyCheck(req, res, next) {
//const authHeader = req.headers.authorization
try {
let apikey = req.headers.authorization;
if (!apikey) {
res.status(401).json({
message: "No API key was supplied. Invalid request",
});
//throw new Error("No API key was supplied. Invalid request");
} else {
//split the string by the -
let splitAPIkey = apikey.split("-");
let rowid = splitAPIkey[0];
//rejoin withouth the rowid
let SuppliedKey = splitAPIkey.slice(1).join("-");
if (checkAPikey(SuppliedKey, rowid)) {
//get permission
let permission = await checkAPikey(SuppliedKey, rowid);
console.log(permission);
if (req.method === "GET" && permission === "canRead") {
return next();
}
//['POST', 'PUT', 'PATCH', 'DELETE'].includes(req.method)
if (
["GET", "POST", "PUT", "DELETE"].includes(req.method) &&
permission === "canWrite"
) {
console.log("write");
return next();
}
//throw status 403
res.status(403).json({
message:
"Your API key does not have the correct permissions to access this resource",
});
}
}
} catch (error) {
next(error);
}
}
module.exports = { apikeyCheck };
/*
//web server microservice
1) take user supplied rowid-apikey
2) split the string by -
3) get the rowid or table id
4) get the apikey
5) compare the apikey with the one in database
6) if match, return true
*/
/*
I plan to seed some data in user and api
Than use the system info and my API middleware will somehow check the supplied API key and check
If it's correct API key and has canWrite perms
I allow it to access put and post
*/

View File

@ -1,31 +1,68 @@
const { apikeyModel } = require("../database/model/apiKeyModel");
const { tokenModel } = require("../database/model/tokenModel");
const { userModel } = require("../database/model/userModel");
const { compareHash } = require("../functions/bcrypt");
const { checkToken } = require("../functions/api");
const { isValid } = require("../functions/isValid");
async function auth(req, res, next){
try{
// let user = await Auth.checkToken({token: req.header('auth-token')});
let authToken = req.header('auth-token');
let splitAuthToken = authToken.split('-');
let rowid = splitAuthToken[0];
let suppliedToken = splitAuthToken.slice(1).join('-');
//get from db
let token = await apikeyModel.findByPk(rowid, {include: userModel});
if (!token) return false;
async function auth(req, res, next) {
try {
const authToken = req.header("auth-token");
if (!authToken) {
const error = new Error("No Token key was supplied. Invalid request");
throw error;
}
//compare
let isMatch = await compareHash(suppliedToken, token.apikey);
if (!isMatch) return false;
const splitAuthToken = authToken.split("-");
const rowid = splitAuthToken[0];
const suppliedToken = splitAuthToken.slice(1).join("-");
//else do logic
//pass hashed token to req.token (IMPORTANT ITS NOT PASSED TO CLIENT)
req.token = token
req.user = await token.getUser(); //taking user seq obj from usermodel
next();
}catch(error){
const token = await tokenModel.findByPk(rowid, { include: userModel });
if (!token) {
const error = new Error("Token key not found. Invalid request");
throw error;
}
const isMatch = await compareHash(suppliedToken, token.token);
console.log(isMatch);
if (!isMatch) {
const error = new Error("Token key not found. Invalid request");
throw error;
}
//if token is a match
req.token = token;
req.user = await token.getUser();
const permission = await checkToken(suppliedToken, rowid);
const route = req.originalUrl.split("?")[0]; // Removing query parameters
//if route is from user/ and permission is canRead allow it to do CRUD
if (route.includes("/user/") && permission === "canRead") {
next();
}
if ((req.method === "GET" && permission === "canRead") || (["GET", "POST", "PUT", "DELETE"].includes(req.method) && permission === "canWrite")) {
next();
}
if (!isValid(token.expiration)){
req.token.destroy();
throw new Error("Token expired");
}
} catch (error) {
next(error);
}
}
module.exports = { auth };
module.exports = { auth };
/*
else {
const error = new Error("Insufficient permission");
error.status = 401;
throw error;
}
*/

View File

@ -9,8 +9,8 @@ let transporter = nodemailer.createTransport({
port: 587,
secure: false,
auth: {
user:
pass:
user: process.env.euser,
pass: process.env.epass
},
});
module.exports = { transporter };

View File

@ -165,6 +165,7 @@ app.socket = (function (app) {
});
return socket;
})(app);
//sensor data
app.sensordata = (function (app) {
@ -183,30 +184,21 @@ app.auth = (function (app) {
}
function isLoggedIn(callback) {
if (getToken()) {
console.log("you shldnt appear at all");
return app.api.get("user/me", function (error, data) {
console.log(error, data);
if (!error) app.auth.user = data;
//for navbar to show username
if (!location.pathname === "/login")
{
$.scope.getUsername.update(data);
}
//for edit profile to show user details
//if not in edit profile page, it will not show
if (location.pathname === "/profile") {
$.scope.getUserDetails.update(data);
}
return callback(error, data);
});
} else {
callback(null, false);
callback(true);
}
}
function logOut(callback) {
//call logout route
console.log("Logging out");
$.ajax({
type: "DELETE",
url: "/api/v0/user/logout",

View File

@ -2,42 +2,21 @@
const router = require('express').Router();
const { auth } = require("../middleware/authChecker")
const { APIlogger } = require('../middleware/apiLogger.js');
const { apikeyCheck } = require('../middleware/apiKey.js');
router.use('/auth', require('./auth'));
router.use('/apikey', require('./apikey'));
router.use('/token', [auth, APIlogger], require('./token.js'));
router.use('/user', [auth, APIlogger], require('./user'));
//TO REFACTOR INTO ONE MIDDLWARE
//location route
router.use('/location', [auth, APIlogger], require('./location.js'));
//location route
router.use('/location', [apikeyCheck , APIlogger], require('./location.js'));
router.use('/sensor', [auth, APIlogger], require('./sensor.js'));
//location route
router.use('/sensor', [apikeyCheck , APIlogger], require('./sensor.js'));
//location route
router.use('/sensor-data', [apikeyCheck, APIlogger], require('./sensorData.js'));
//sensor data route
router.use('/sensor-data', [auth, APIlogger], require('./sensorData.js'));
module.exports = router;
/*
'use strict';
const router = require('express').Router();
const { auth } = require("../middleware/authChecker")
router.use('/auth', require('./auth'));
router.use('/apikey', require('./apikey'));
router.use('/user', auth ,require('./user'));
module.exports = router;
*/

View File

@ -51,7 +51,6 @@ router.post("/login", async (req, res, next) => {
//contact
//auth/contact
router.post("/contact", async (req, res, next) => {
});

View File

@ -25,7 +25,6 @@ router.get("/", async (req, res, next) => {
//add location
router.post("/new", async (req, res, next) => {
try {
console.log(req.body);
const { name, added_by, description } = req.body;
await addLocation(name, added_by, description);
res.sendStatus(200)

View File

@ -1,42 +1,3 @@
/*
'use strict';
var router = require('express').Router();
const conf = require('../conf')
const values ={
title: conf.environment !== 'production' ? `<i class="fa-brands fa-dev"></i>` : ''
}
router.get('/', async function(req, res, next) {
res.render('runner', {...values});
});
router.get('/topics', function(req, res, next) {
res.render('topics', {...values});
});
router.get('/chat', function(req, res, next) {
res.render('chat', {...values});
});
router.get('/login*', function(req, res, next) {
res.render('login', {redirect: req.query.redirect, ...values});
});
router.get('/runner', function(req, res, next) {
res.render('runner', {...values});
});
router.get('/worker', function(req, res, next) {
res.render('worker', {...values});
});
module.exports = router;
*/
"use strict";
var router = require("express").Router();

View File

@ -1,4 +1,4 @@
const { addAPIKey } = require("../functions/api");
const { addToken } = require("../functions/api");
const express = require("express");
@ -10,14 +10,13 @@ const router = express.Router();
3) hash the api key
4) store the api key in database
*/
//token/new
//curl localhost:3000/api/v0/token/new -H "Content-Type: application/json" -X POST -d
//'{"userid": "5", "permission": "canRead" ,}'
router.post("/new", async (req, res, next) => {
try {
//curl localhost/api/v0/apikey/new -H "Content-Type: application/json" -X POST -d
//'{"userid": 1, "permission": "canWrite"}'
const apikey = await addAPIKey(req.body.userid, req.body.permission);
//console.log(typeof req.body.userid);
//console.log(typeof req.body.permission);
res.json({apikey: apikey});
const token = await addToken(req.body.userid, req.body.permission , "2204-01-24 07:34:36" );
res.json({token: token});
} catch (error) {
console.error(error);
next(error);

View File

@ -1,4 +1,4 @@
const { getUserID, updateProfile } = require("../functions/user");
const { getUserByID, updateProfile } = require("../functions/user");
const express = require("express");
const router = express.Router();
@ -7,12 +7,8 @@ const router = express.Router();
//getbyid
router.get("/me", async function (req, res, next) {
try {
let user = await getUserID(req.user);
if (!user) {
let error = new Error("User not found");
error.status = 400;
return next(error);
}
let user = await getUserByID(req.user);
console.log(user);
res.json({
user: user,
});

View File

@ -1,4 +1,5 @@
<%- include('top') %>
<br>
<br>
<br>

View File

@ -1,41 +1,39 @@
<%- include('top') %>
<style>
#sensorDataList{
padding-top: 2.5em;
}
</style>
<style>
#sensorDataList {
padding-top: 2.5em;
}
</style>
<ul id="sensorDataList">
<li jq-repeat='sensorData'>
rowid: {{ id }}
sensorId: {{ sensorid }}
created: {{ createdAt }}
location: {{ locationid }}
<br/ >
co: {{ measurement.co }}
humidity: {{ measurement.humidity }}
no2: {{ measurement.no2 }}
o3: {{ measurement.o3 }}
psi: {{ measurement.psi }}
so2: {{ measurement.so2 }}
temperature: {{ measurement.temperature }}
windspeed: {{ measurement.windspeed }}
<hr />
</li>
<li jq-repeat-defualt='sensorData'>
Loading...
</li>
</ul>
<ul id="sensorDataList">
<li jq-repeat='sensorData'>
rowid: {{ id }}
sensorId: {{ sensorid }}
created: {{ createdAt }}
location: {{ locationid }}
<br />
co: {{ measurement.co }}
humidity: {{ measurement.humidity }}
no2: {{ measurement.no2 }}
o3: {{ measurement.o3 }}
psi: {{ measurement.psi }}
so2: {{ measurement.so2 }}
temperature: {{ measurement.temperature }}
windspeed: {{ measurement.windspeed }}
<hr />
</li>
<li jq-repeat-defualt='sensorData'>
Loading...
</li>
</ul>
<script type="text/javascript">
$(document).ready(async function(){
$.scope.sensorData.push(...await app.api.get('sensor-data/data?order=DESC&limit=40'));
<script type="text/javascript">
$(document).ready(async function () {
app.api.get('sensor-data/data?order=DESC&limit=40', function(error, data){
$.scope.sensorData.push(...data);
})
})
app.socket.on('sensordata:new', function(data){
$.scope.sensorData.unshift(data);
});
})
</script>
<%- include('bot') %>
</script>
<%- include('bot') %>

View File

@ -56,7 +56,13 @@
$(document).ready(function () {
//check if user is logged in
app.auth.isLoggedIn(function (error, data) {
if (data) {
if (!error) {
console.log(error);
$.scope.getUsername.update(data);
if (location.pathname == "/profile") {
$.scope.getUserDetails.update(data);
}
$("#cl-logout-button").show("fast");
$("#cl-api-button").show("fast");
$("#cl-profile-button").show("fast");
@ -127,7 +133,4 @@
</ul>
</div>
</div>
</nav>
</body>
</html>
</nav>

View File

@ -1,43 +1,4 @@
//import('dotenv').config({ path: path.resolve(__dirname, '../../../.env') })
document.addEventListener('DOMContentLoaded', () => {
const form = document.getElementById('form');
// Set the new value for the access_key input field
form.querySelector('input[name="access_key"]').value = process.env.emailKey;
form.addEventListener('submit', async (event) => {
event.preventDefault(); // Prevent default form submission
// Create a FormData object to include the key
const formData = new FormData(form);
// Submit the form using fetch API
try {
const response = await fetch('https://api.web3forms.com/submit', {
method: 'POST',
body: formData
});
const result = await response.json();
// Handle the API response
console.log(result);
if (result.success) {
// Form submitted successfully, display notification
alert('Form submitted successfully!');
location.reload()
} else {
// Form submission failed, display error notification
alert('Form submission failed. Please try again.');
}
} catch (error) {
//console.error('Error:', error);
}
});
});
moment = require('moment')
//current time
console.log(moment().format('hh:mm:ss a'))

3597
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -17,6 +17,7 @@
},
"homepage": "https://github.com/Newtbot/MP#readme",
"dependencies": {
"axios": "^1.6.5",
"bcrypt": "^5.1.1",
"body-parser": "^1.20.2",
"cookie-parser": "^1.4.6",