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"); const client = require("./modules/mqtt");
async function publishData() { async function publishData() {
try { try {
const data = await run(); let iothub = new IoTdataGenerator();
let data = await iothub.generateData();
console.log(data); console.log(data);
client.publish("iot-data", JSON.stringify(data)); client.publish("iot-data", JSON.stringify(data));
} catch (err) { } catch (err) {

View File

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

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 express = require("express");
const session = require("express-session"); const session = require("express-session");
const rateLimit = require('express-rate-limit');
const cookieParser = require('cookie-parser'); const cookieParser = require('cookie-parser');
const bodyParser = require("body-parser"); const bodyParser = require("body-parser");
const bcrypt = require("bcrypt"); const bcrypt = require("bcrypt");
const crypto = require("crypto"); const crypto = require("crypto");
const nodemailer = require("nodemailer");
const otpGenerator = require('otp-generator');
const { body, validationResult } = require('express-validator');
const validator = require('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 { format } = require('date-fns');
const helmet = require('helmet');
const { Sequelize } = require('sequelize'); const { Sequelize } = require('sequelize');
const { transporter } = require("./modules/nodeMailer"); const { transporter } = require("./modules/nodeMailer");
const { sequelize, User } = require("./modules/mysql"); 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 app = express();
const nonce = crypto.randomBytes(16).toString('base64');
console.log('Nonce:', nonce);
app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json()); app.use(bodyParser.json());
app.use(cookieParser()); app.use(cookieParser());
@ -30,17 +29,6 @@ require("dotenv").config();
app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.urlencoded({ extended: true }));
app.set("view engine", "ejs"); 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({ app.use(session({
secret: process.env.key, secret: process.env.key,
@ -56,79 +44,33 @@ function isAuthenticated(req, res, next) {
if (req.session && req.session.authenticated) { if (req.session && req.session.authenticated) {
return next(); return next();
} else { } else {
res.redirect("/login"); res.redirect("/index");
} }
} }
const generateOTP = () => { app.get('/index', (req, res) => {
const otp = otpGenerator.generate(6, { upperCase: false, specialChars: false }); res.render('index');
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("/login", (req, res) => { app.get("/login", (req, res) => {
res.render("login", { error: null }); res.render("login", { error: null });
}); });
const limiter = rateLimit({ app.use('/login', 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.post('/login', loginValidation, async (req, res) => {
app.post('/login', [ try {const errors = validationResult(req);
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);
if (!errors.isEmpty()) { if (!errors.isEmpty()) {
return res.render('login', { error: 'Invalid input. Please check your credentials.', csrfToken: req.session.csrfToken }); return res.render('login', { error: 'Invalid input. Please check your credentials.', csrfToken: req.session.csrfToken });
} }
let { username, password } = req.body; let { username, password } = req.body;
username = username.trim(); username = username.trim();
const user = await User.findOne({ where: { username } }); const user = await User.findOne({ where: { username } });
if (user) { if (user) {
const isLoginSuccessful = await bcrypt.compare(password, user.password); const isLoginSuccessful = await bcrypt.compare(password, user.password);
if (isLoginSuccessful) { if (isLoginSuccessful) {
await userLogs.create({ username, success: true, activity: "Credentials entered correctly" }); await userLogs.create({ username, success: true, activity: "Credentials entered correctly" });
const { otp, expirationTime } = generateOTP(); const { otp, expirationTime } = generateOTP();
req.session.otp = otp; req.session.otp = otp;
@ -174,17 +116,12 @@ async (req, res) => {
// OTP verification route // OTP verification route
app.post("/verify-otp", [ app.post("/verify-otp", otpValidation ,async (req, res) => {
body('otp').escape().trim().isLength({ min: 1 }).withMessage('OTP must not be empty'),
],
async (req, res) => {
try { try {
const errors = validationResult(req); const errors = validationResult(req);
if (!errors.isEmpty()) { if (!errors.isEmpty()) {
return res.render('otp', { error: 'Invalid OTP. Please try again.'}); return res.render('otp', { error: 'Invalid OTP. Please try again.'});
} }
const enteredOTP = req.body.otp; const enteredOTP = req.body.otp;
if (!req.session) { if (!req.session) {
@ -222,17 +159,10 @@ app.post("/verify-otp", [
req.session.authenticated = true; req.session.authenticated = true;
req.session.username = req.body.username; req.session.username = req.body.username;
req.session.sessionToken = sessionToken; req.session.sessionToken = sessionToken;
csrfTokenSession = crypto.randomBytes(32).toString('hex'); 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 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"); res.redirect("/home");
} else { } else {
if (req.body.username) { if (req.body.username) {
@ -275,17 +205,11 @@ app.post("/verify-otp", [
} }
}); });
app.get("/home", isAuthenticated, async (req, res) => {
app.get("/home", isAuthenticated, (req, res) => { res.render("home", { username: req.session.username});
// Render the home page with sensor data
res.render("home", {
username: req.session.username,
});
}); });
app.get("/inusers", isAuthenticated, async (req, res) => { app.get("/inusers", isAuthenticated, async (req, res) => {
try { try {
// Fetch all user data from the database using Sequelize // Fetch all user data from the database using Sequelize
@ -294,9 +218,8 @@ app.post("/verify-otp", [
}); });
const currentUsername = req.session.username; const currentUsername = req.session.username;
// Render the inusers page with JSON data // 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) { } catch (error) {
console.error("Error fetching all users:", error); console.error("Error fetching all users:", error);
res.status(500).send("Internal Server Error"); res.status(500).send("Internal Server Error");
@ -332,20 +255,8 @@ function isStrongPassword(password) {
return true; return true;
} }
app.post( app.post(
'/createUser', '/createUser', createValidation, async (req, res) => {
[
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) => {
try { try {
const errors = validationResult(req); const errors = validationResult(req);
@ -477,24 +388,13 @@ app.post("/forgot-password", async (req, res) => {
const error = "Username or email not found."; const error = "Username or email not found.";
return res.render("forgot-password", { error, success: null }); return res.render("forgot-password", { error, success: null });
} }
// Generate reset token and update the user // Generate reset token and update the user
const reset_token = crypto.randomBytes(20).toString("hex"); const reset_token = crypto.randomBytes(20).toString("hex");
const reset_token_expiry = new Date(Date.now() + 3600000); // Token expires in 1 hour const reset_token_expiry = new Date(Date.now() + 3600000); // Token expires in 1 hour
// Update the user with the reset token and expiry // Update the user with the reset token and expiry
await User.update( await User.update({reset_token,reset_token_expiry,},
{ {where: {id: user.id},}
reset_token,
reset_token_expiry,
},
{
where: {
id: user.id, // Replace 'id' with the actual primary key field of your User model
},
}
); );
// Send email with reset link // Send email with reset link
const resetLink = `http://localhost:3000/reset-password/${reset_token}`; const resetLink = `http://localhost:3000/reset-password/${reset_token}`;
const mailOptions = { const mailOptions = {
@ -502,12 +402,9 @@ app.post("/forgot-password", async (req, res) => {
subject: "Password Reset", subject: "Password Reset",
text: `Click on the following link to reset your password: ${resetLink}`, text: `Click on the following link to reset your password: ${resetLink}`,
}; };
await transporter.sendMail(mailOptions); await transporter.sendMail(mailOptions);
const success = "Password reset email sent successfully. Check your inbox."; const success = "Password reset email sent successfully. Check your inbox.";
res.render("forgot-password", { error: null, success }); res.render("forgot-password", { error: null, success });
// Log the successful sending of the reset link in the database // Log the successful sending of the reset link in the database
await userLogs.create({ await userLogs.create({
username: user.username, username: user.username,
@ -523,29 +420,22 @@ app.post("/forgot-password", async (req, res) => {
console.error("Error during password reset:", error); console.error("Error during password reset:", error);
const errorMessage = "An error occurred during the password reset process."; const errorMessage = "An error occurred during the password reset process.";
res.render("forgot-password", { error: errorMessage, success: null }); res.render("forgot-password", { error: errorMessage, success: null });
} }});
});
app.post("/reset-password/:token", async (req, res) => { app.post("/reset-password/:token", async (req, res) => {
try { try {
const { token } = req.params; const { token } = req.params;
const { password, confirmPassword } = req.body; const { password, confirmPassword } = req.body;
// Sanitize the inputs // Sanitize the inputs
const sanitizedToken = validator.escape(token); const sanitizedToken = validator.escape(token);
const sanitizedPassword = validator.escape(password); const sanitizedPassword = validator.escape(password);
const sanitizedConfirmPassword = validator.escape(confirmPassword); const sanitizedConfirmPassword = validator.escape(confirmPassword);
// Find user with matching reset token and not expired // Find user with matching reset token and not expired
const user = await User.findOne({ const user = await User.findOne({
where: { where: {reset_token: sanitizedToken,
reset_token: sanitizedToken, reset_token_expiry: { [Sequelize.Op.gt]: new Date() },
reset_token_expiry: { [Sequelize.Op.gt]: new Date() },
}, },
}); });
if (!user) { if (!user) {
// Pass the error to the template when rendering the reset-password page // Pass the error to the template when rendering the reset-password page
return res.render("reset-password", { return res.render("reset-password", {
@ -553,7 +443,6 @@ app.post("/forgot-password", async (req, res) => {
resetError: "Invalid or expired reset token", resetError: "Invalid or expired reset token",
}); });
} }
// Check if passwords match // Check if passwords match
if (sanitizedPassword !== sanitizedConfirmPassword) { if (sanitizedPassword !== sanitizedConfirmPassword) {
// Pass the error to the template when rendering the reset-password page // 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", resetError: "Passwords do not match",
}); });
} }
// Check if the new password meets complexity requirements // Check if the new password meets complexity requirements
if (!isStrongPassword(sanitizedPassword)) { if (!isStrongPassword(sanitizedPassword)) {
// Pass the error to the template when rendering the reset-password page // Pass the error to the template when rendering the reset-password page
return res.render("reset-password", { return res.render("reset-password", {
token, token, resetError:
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.", "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 // Hash the new password
const saltRounds = 10; const saltRounds = 10;
const hashedPassword = await bcrypt.hash(sanitizedPassword, saltRounds); const hashedPassword = await bcrypt.hash(sanitizedPassword, saltRounds);
// Update user's password and clear reset token // Update user's password and clear reset token
const updateQuery = { const updateQuery = {
password: hashedPassword, password: hashedPassword,
reset_token: null, reset_token: null,
reset_token_expiry: null, reset_token_expiry: null,
}; };
const whereCondition = { const whereCondition = {reset_token: sanitizedToken,};
reset_token: sanitizedToken,
};
await User.update(updateQuery, { await User.update(updateQuery, {
where: whereCondition, where: whereCondition,
}); });
@ -633,10 +515,8 @@ app.post("/reset-password", async (req, res) => {
return res.status(403).json({ error: 'CSRF token mismatch' }); return res.status(403).json({ error: 'CSRF token mismatch' });
} }
const sessionTokencookie = req.cookies['sessionToken']; const sessionTokencookie = req.cookies['sessionToken'];
// Verify sessionToken with the one stored in the database // Verify sessionToken with the one stored in the database
const user = await User.findOne({ where: { sessionid: sessionTokencookie } }); const user = await User.findOne({ where: { sessionid: sessionTokencookie } });
if (!user) { if (!user) {
return res.status(403).json({ error: 'Invalid sessionToken' }); 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 sanitizedUsername = validator.escape(username);
const sanitizedPassword = validator.escape(password); const sanitizedPassword = validator.escape(password);
const sanitizedConfirmPassword = validator.escape(confirmPassword); const sanitizedConfirmPassword = validator.escape(confirmPassword);
// Check if passwords match // Check if passwords match
if (sanitizedPassword !== sanitizedConfirmPassword) { if (sanitizedPassword !== sanitizedConfirmPassword) {
return res.status(400).json({ error: "Passwords do not match" }); return res.status(400).json({ error: "Passwords do not match" });
} }
// Check if the new password meets complexity requirements // Check if the new password meets complexity requirements
if (!isStrongPassword(sanitizedPassword)) { if (!isStrongPassword(sanitizedPassword)) {
return res.status(400).json({ 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.", "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 { try {
// Find the user in the database // Find the user in the database
const user = await User.findOne({ where: { username: sanitizedUsername } }); const user = await User.findOne({ where: { username: sanitizedUsername } });
if (!user) { if (!user) {
return res.status(404).json({ error: "User does not exist" }); return res.status(404).json({ error: "User does not exist" });
} }
// Generate a random salt and hash the new password // Generate a random salt and hash the new password
const saltRounds = 10; const saltRounds = 10;
const hashedPassword = await bcrypt.hash(sanitizedPassword, saltRounds); const hashedPassword = await bcrypt.hash(sanitizedPassword, saltRounds);
// Update user's password // Update user's password
await User.update( await User.update(
{ password: hashedPassword }, { password: hashedPassword },
{ where: { username: sanitizedUsername } } { where: { username: sanitizedUsername } }
); );
// Log password reset activity // Log password reset activity
await userLogs.create({ await userLogs.create({
username: creatorUsername, username: creatorUsername,
activity: `Password has been reset for ${sanitizedUsername}`, activity: `Password has been reset for ${sanitizedUsername}`,
}); });
// Password update successful // Password update successful
return res.status(200).json({ success: "Password updated successfully" }); return res.status(200).json({ success: "Password updated successfully" });
} catch (error) { } catch (error) {
@ -693,10 +565,8 @@ app.post("/reset-password", async (req, res) => {
app.get('/searchUser', async (req, res) => { app.get('/searchUser', async (req, res) => {
const { username } = req.query; const { username } = req.query;
// Sanitize the input // Sanitize the input
const sanitizedUsername = validator.escape(username); const sanitizedUsername = validator.escape(username);
try { try {
// Find the user in the database // Find the user in the database
const user = await User.findOne({ where: { username: sanitizedUsername } }); const user = await User.findOne({ where: { username: sanitizedUsername } });
@ -704,11 +574,7 @@ app.get('/searchUser', async (req, res) => {
if (!user) { if (!user) {
// No user found with the given username // No user found with the given username
res.status(404).json({ success: false, error: 'User not found' }); res.status(404).json({ success: false, error: 'User not found' });
} else { } else {res.json(user)}
// User found, return user data
res.json(user);
}
} catch (error) { } catch (error) {
console.error('Sequelize query error:', error); console.error('Sequelize query error:', error);
res.status(500).json({ success: false, error: 'Internal Server Error' }); res.status(500).json({ success: false, error: 'Internal Server Error' });
@ -719,7 +585,6 @@ app.get('/api/users', async (req, res) => {
try { try {
// Find all users in the database // Find all users in the database
const users = await User.findAll(); const users = await User.findAll();
// Return the users in the response // Return the users in the response
res.json(users); res.json(users);
} catch (error) { } 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 }); res.status(500).json({ success: false, error: 'Internal Server Error', details: error.message });
} }
}); });
app.get('/api/getLogs', async (req, res) => { app.get('/api/getLogs', async (req, res) => {
try { 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.use(express.static("views"));
app.listen(PORT, () => { 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"> <div class="input-box">
<span class="details">Username</span> <span class="details">Username or Email</span>
<input type="text" name="usernameOrEmail" placeholder="Enter your email" required> <input type="text" name="usernameOrEmail" placeholder="Enter your Username or Email" required>
</div> </div>
<div class="button"> <div class="button">

View File

@ -20,7 +20,8 @@
<nav> <nav>
<a href="/inusers">In-House Users</a> <a href="/inusers">In-House Users</a>
<a href="#">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> <a href="/logout">Logout</a>
</nav> </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> </div>
<script nonce="<%= nonce %>"> <script >
const allUsers = <%- JSON.stringify(allUsers) %>; const allUsers = <%- JSON.stringify(allUsers) %>;
const currentUsername = '<%= currentUsername %>'; const currentUsername = '<%= currentUsername %>';
const nonce = "<%= nonce %>"
console.log('Nonce:', nonce);
</script> </script>
<script src="https://code.jquery.com/jquery-3.6.4.min.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" nonce="<%= nonce %>" ></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" nonce="<%= nonce %>"></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" nonce="<%= nonce %>"></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" nonce="<%= nonce %>"></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" nonce="<%= nonce %>"></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" nonce="<%= nonce %>"></script> <script src="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.js"></script>
<script src="inusers.js" nonce="<%= nonce %>"></script> <script src="inusers.js"></script>
</body> </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 */ 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 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/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; module.exports = app;
process.nextTick(() => require("./mqttApp"));
app.use(express.json()); app.use(express.json());
app.set("json spaces", 2); app.set("json spaces", 2);
@ -14,12 +16,16 @@ app.set("json spaces", 2);
const limiter = rateLimit({ const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes windowMs: 15 * 60 * 1000, // 15 minutes
limit: 600, // Limit each IP to 100 requests per `window` (here, per 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. legacyHeaders: false, // Disable the `X-RateLimit-*` headers.
}); });
// Hold list of functions to run when the server is ready // 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. // Apply the rate limiting middleware to all requests.
app.use(limiter); app.use(limiter);
@ -34,10 +40,9 @@ app.set("view engine", "ejs");
// Have express server static content( images, CSS, browser JS) from the public // Have express server static content( images, CSS, browser JS) from the public
app.use(express.static(path.join(__dirname, "./public"))); app.use(express.static(path.join(__dirname, "./public")));
//route logic //route logic
app.use("/api/seed/v0" ,require("./routes/seed_route.js")); app.use("/api/seed/v0", require("./routes/seed_route.js"));
app.use("/api/v0", require("./routes/api_routes")); app.use("/api/v0", require("./routes/api_routes"));
//render logic //render logic
app.use("/", require("./routes/render")); 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 // Catch 404 and forward to error handler. If none of the above routes are
// used, this is what will be called. // used, this is what will be called.
app.use(function (req, res, next) { app.use(function (req, res, next) {
if (req.is("application/json")) { //application/json; charset=utf-8
var err = new Error("Not Found"); if (req.is("application/json" || "application/json; charset=utf-8")) {
err.message = "Page not found"; var err = new Error("Not Found");
err.status = 404; err.message = "Page not found";
next(err); err.status = 404;
} next(err);
else{ } else {
res.status(404).render("404"); res.status(404).render("404");
} }
}); });
// Error handler. This is where `next()` will go on error // Error handler. This is where `next()` will go on error
app.use(function (err, req, res, next) { app.use(function (err, req, res, next) {
console.error(err.status || res.status, err.name, req.method, req.url); 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; 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); res.status(err.status || 500);
console.log(keyErrors);
res.json({ res.json({
name: err.name, name: err.name || "Unknown error",
message: err.message, message: err.message,
keyErrors, keyErrors,
}); });
}); });

View File

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

View File

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

View File

@ -1,5 +1,5 @@
const { hash, compareHash } = require("./bcrypt.js"); const { hash, compareHash } = require("./bcrypt.js");
const { apikeyModel } = require("../database/model/apiKeyModel"); const { tokenModel } = require("../database/model/tokenModel.js");
const { generateUUID } = require("./generateUUID.js"); const { generateUUID } = require("./generateUUID.js");
/* /*
@ -11,37 +11,38 @@ const { generateUUID } = require("./generateUUID.js");
6) store in database 6) store in database
*/ */
//can be used for api key or token. Both are the same logic //can be used for api key or token. Both are the same logic
async function addAPIKey(userId, permission) { async function addToken(userId, permission , expiry) {
let hashtoken = await generateUUID(); let uuid = await generateUUID();
let apikey = await hash(hashtoken); let hashtoken = await hash(uuid);
let token = await apikeyModel.create({ let token = await tokenModel.create({
userid: userId, userid: userId,
apikey: apikey, token: hashtoken,
permission: permission, permission: permission,
expiration: expiry,
}); });
//user token with - tokenid is table id //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 { try {
const retrivedKey = await apikeyModel.findOne({ const retrivedToken = await tokenModel.findOne({
raw: true, raw: true,
attributes: ["apikey", "permission"], attributes: ["token", "permission"],
where: { where: {
id: rowid, id: rowid,
}, },
}); });
//console.log(retrivedKey.apikey); //console.log(retrivedKey.apikey);
if (compareHash(SuppliedKey, retrivedKey.apikey)) { if (compareHash(Supplied, retrivedToken.token)) {
//return true; //return true;
return retrivedKey.permission; return retrivedToken.permission;
} }
} catch (error) { } catch (error) {
console.error(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) { async function addLocation(name, added_by, description) {
console.log(name, added_by, description);
const location = await locationModel.create({ const location = await locationModel.create({
name: name, name: name,
added_by: added_by, added_by: added_by,
@ -30,7 +29,6 @@ async function updateLocation(id, name, added_by, description) {
} }
async function deleteLocation(id) { async function deleteLocation(id) {
//delete by id
const location = await locationModel.destroy({ const location = await locationModel.destroy({
where: { where: {
id: id, id: id,
@ -38,6 +36,7 @@ async function deleteLocation(id) {
}); });
} }
async function getLocationById(id) { async function getLocationById(id) {
const location = await locationModel.findAll({ const location = await locationModel.findAll({
where: { where: {

View File

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

View File

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

View File

@ -1,13 +1,14 @@
const { Op } = require('sequelize') const { Op } = require('sequelize')
const { hash, compareHash } = require("./bcrypt.js"); const { hash, compareHash } = require("./bcrypt.js");
const { addAPIKey } = require("./api"); const { addToken } = require("./api");
const { userModel } = require("../database/model/userModel"); const { userModel } = require("../database/model/userModel");
moment = require('moment')
//getuser //getuser
//api/v0/user/me //api/v0/user/me
async function getUserID(userid) { async function getUserByID(userid) {
//console.log(userid); //console.log(userid);
//console.log(userid.id); //console.log(userid.id);
let userRes = await userModel.findByPk(userid.id, { let userRes = await userModel.findByPk(userid.id, {
@ -20,28 +21,6 @@ async function getUserID(userid) {
return userRes; 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 //api/v0/auth/register
/* Registering new user /* Registering new user
1) req.body is taken from html form or wtv 1) req.body is taken from html form or wtv
@ -92,9 +71,9 @@ async function loginUser(user) {
if (!match) return false; if (!match) return false;
//console.log('loginUser', userRes.id, userRes.username); //console.log('loginUser', userRes.id, userRes.username);
//generate token //generate token and permission and experiation time
let token = await addAPIKey(userRes.id, "auto-generated"); 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 }; return { token: token, userid: userRes.id, username: userRes.username };
} }
@ -152,7 +131,7 @@ async function updateProfile(user, body) {
} }
module.exports = { module.exports = {
getUserID, getUserByID,
addUser, addUser,
loginUser, loginUser,
updateProfile, 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 { userModel } = require("../database/model/userModel");
const { compareHash } = require("../functions/bcrypt"); 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 async function auth(req, res, next) {
let token = await apikeyModel.findByPk(rowid, {include: userModel}); try {
if (!token) return false; const authToken = req.header("auth-token");
if (!authToken) {
const error = new Error("No Token key was supplied. Invalid request");
throw error;
}
//compare const splitAuthToken = authToken.split("-");
let isMatch = await compareHash(suppliedToken, token.apikey); const rowid = splitAuthToken[0];
if (!isMatch) return false; const suppliedToken = splitAuthToken.slice(1).join("-");
//else do logic const token = await tokenModel.findByPk(rowid, { include: userModel });
//pass hashed token to req.token (IMPORTANT ITS NOT PASSED TO CLIENT)
req.token = token if (!token) {
req.user = await token.getUser(); //taking user seq obj from usermodel const error = new Error("Token key not found. Invalid request");
next(); throw error;
}catch(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); 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, port: 587,
secure: false, secure: false,
auth: { auth: {
user: user: process.env.euser,
pass: pass: process.env.epass
}, },
}); });
module.exports = { transporter }; module.exports = { transporter };

View File

@ -165,6 +165,7 @@ app.socket = (function (app) {
}); });
return socket; return socket;
})(app); })(app);
//sensor data //sensor data
app.sensordata = (function (app) { app.sensordata = (function (app) {
@ -183,30 +184,21 @@ app.auth = (function (app) {
} }
function isLoggedIn(callback) { function isLoggedIn(callback) {
if (getToken()) { if (getToken()) {
console.log("you shldnt appear at all");
return app.api.get("user/me", function (error, data) { return app.api.get("user/me", function (error, data) {
console.log(error, data);
if (!error) app.auth.user = 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); return callback(error, data);
}); });
} else { } else {
callback(null, false); callback(true);
} }
} }
function logOut(callback) { function logOut(callback) {
//call logout route console.log("Logging out");
$.ajax({ $.ajax({
type: "DELETE", type: "DELETE",
url: "/api/v0/user/logout", url: "/api/v0/user/logout",

View File

@ -2,42 +2,21 @@
const router = require('express').Router(); const router = require('express').Router();
const { auth } = require("../middleware/authChecker") const { auth } = require("../middleware/authChecker")
const { APIlogger } = require('../middleware/apiLogger.js'); const { APIlogger } = require('../middleware/apiLogger.js');
const { apikeyCheck } = require('../middleware/apiKey.js');
router.use('/auth', require('./auth')); 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')); router.use('/user', [auth, APIlogger], require('./user'));
//TO REFACTOR INTO ONE MIDDLWARE //location route
router.use('/location', [auth, APIlogger], require('./location.js'));
//location route //location route
router.use('/location', [apikeyCheck , APIlogger], require('./location.js')); router.use('/sensor', [auth, APIlogger], require('./sensor.js'));
//location route //sensor data route
router.use('/sensor', [apikeyCheck , APIlogger], require('./sensor.js')); router.use('/sensor-data', [auth, APIlogger], require('./sensorData.js'));
//location route
router.use('/sensor-data', [apikeyCheck, APIlogger], require('./sensorData.js'));
module.exports = router; 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 //contact
//auth/contact //auth/contact
router.post("/contact", async (req, res, next) => { router.post("/contact", async (req, res, next) => {
}); });

View File

@ -25,7 +25,6 @@ router.get("/", async (req, res, next) => {
//add location //add location
router.post("/new", async (req, res, next) => { router.post("/new", async (req, res, next) => {
try { try {
console.log(req.body);
const { name, added_by, description } = req.body; const { name, added_by, description } = req.body;
await addLocation(name, added_by, description); await addLocation(name, added_by, description);
res.sendStatus(200) 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"; "use strict";
var router = require("express").Router(); 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"); const express = require("express");
@ -10,14 +10,13 @@ const router = express.Router();
3) hash the api key 3) hash the api key
4) store the api key in database 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) => { router.post("/new", async (req, res, next) => {
try { try {
//curl localhost/api/v0/apikey/new -H "Content-Type: application/json" -X POST -d const token = await addToken(req.body.userid, req.body.permission , "2204-01-24 07:34:36" );
//'{"userid": 1, "permission": "canWrite"}' res.json({token: token});
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});
} catch (error) { } catch (error) {
console.error(error); console.error(error);
next(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 express = require("express");
const router = express.Router(); const router = express.Router();
@ -7,12 +7,8 @@ const router = express.Router();
//getbyid //getbyid
router.get("/me", async function (req, res, next) { router.get("/me", async function (req, res, next) {
try { try {
let user = await getUserID(req.user); let user = await getUserByID(req.user);
if (!user) { console.log(user);
let error = new Error("User not found");
error.status = 400;
return next(error);
}
res.json({ res.json({
user: user, user: user,
}); });

View File

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

View File

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

View File

@ -56,7 +56,13 @@
$(document).ready(function () { $(document).ready(function () {
//check if user is logged in //check if user is logged in
app.auth.isLoggedIn(function (error, data) { 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-logout-button").show("fast");
$("#cl-api-button").show("fast"); $("#cl-api-button").show("fast");
$("#cl-profile-button").show("fast"); $("#cl-profile-button").show("fast");
@ -127,7 +133,4 @@
</ul> </ul>
</div> </div>
</div> </div>
</nav> </nav>
</body>
</html>

View File

@ -1,43 +1,4 @@
//import('dotenv').config({ path: path.resolve(__dirname, '../../../.env') }) moment = require('moment')
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);
}
});
});
//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", "homepage": "https://github.com/Newtbot/MP#readme",
"dependencies": { "dependencies": {
"axios": "^1.6.5",
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"body-parser": "^1.20.2", "body-parser": "^1.20.2",
"cookie-parser": "^1.4.6", "cookie-parser": "^1.4.6",