Added a prettier web interface placeholder
This commit is contained in:
parent
99c429872b
commit
4f68951725
5
.gitignore
vendored
5
.gitignore
vendored
@ -10,6 +10,11 @@
|
|||||||
*.userosscache
|
*.userosscache
|
||||||
*.sln.docstates
|
*.sln.docstates
|
||||||
|
|
||||||
|
# Tubio files
|
||||||
|
config.json
|
||||||
|
log.txt
|
||||||
|
log.json
|
||||||
|
|
||||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
*.userprefs
|
*.userprefs
|
||||||
|
|
||||||
|
62
Tubio/FileSystem.cpp
Normal file
62
Tubio/FileSystem.cpp
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#include "Filesystem.h"
|
||||||
|
|
||||||
|
std::string FileSystem::ReadFile(std::string filename)
|
||||||
|
{
|
||||||
|
std::ifstream ifs;
|
||||||
|
ifs.open(filename, std::ios::in);
|
||||||
|
if (!ifs.good())
|
||||||
|
{
|
||||||
|
throw std::exception("no such file");
|
||||||
|
std::terminate();
|
||||||
|
}
|
||||||
|
std::string buf;
|
||||||
|
std::stringstream content;
|
||||||
|
while (std::getline(ifs, buf))
|
||||||
|
{
|
||||||
|
content << buf;
|
||||||
|
}
|
||||||
|
return content.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileSystem::WriteFile(std::string filename, std::string content)
|
||||||
|
{
|
||||||
|
std::ofstream ofs;
|
||||||
|
if (!ofs.good()) return false;
|
||||||
|
ofs.open(filename, std::ios::out);
|
||||||
|
ofs << content;
|
||||||
|
ofs.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileSystem::Exists(std::string filename)
|
||||||
|
{
|
||||||
|
std::ifstream ifs;
|
||||||
|
ifs.open(filename, std::ios::in);
|
||||||
|
if (!ifs.good()) return false;
|
||||||
|
ifs.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileSystem::Copy(std::string from, std::string to)
|
||||||
|
{
|
||||||
|
std::ifstream ifs;
|
||||||
|
if (!ifs.good()) return false;
|
||||||
|
ifs.open(from, std::ios::in | std::ios::binary);
|
||||||
|
|
||||||
|
std::ofstream ofs;
|
||||||
|
if (!ofs.good()) return false;
|
||||||
|
ofs.open(to, std::ios::out | std::ios::binary);
|
||||||
|
|
||||||
|
std::copy(std::istreambuf_iterator<char>(ifs),
|
||||||
|
std::istreambuf_iterator<char>(),
|
||||||
|
std::ostreambuf_iterator<char>(ofs));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileSystem::Delete(std::string filename)
|
||||||
|
{
|
||||||
|
if (!Exists(filename)) return false;
|
||||||
|
remove(filename.c_str());
|
||||||
|
return true;
|
||||||
|
}
|
20
Tubio/FileSystem.h
Normal file
20
Tubio/FileSystem.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
class FileSystem
|
||||||
|
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static std::string ReadFile(std::string filename);
|
||||||
|
static bool WriteFile(std::string filename, std::string content);
|
||||||
|
static bool Exists(std::string filename);
|
||||||
|
static bool Copy(std::string from, std::string to);
|
||||||
|
static bool Delete(std::string filename);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
};
|
||||||
|
|
@ -11,7 +11,7 @@ Framework::Framework()
|
|||||||
log->cout << "Starting Tubio server...";
|
log->cout << "Starting Tubio server...";
|
||||||
log->Flush();
|
log->Flush();
|
||||||
|
|
||||||
restInterface = new RestInterface();
|
httpServer = new HttpServer();
|
||||||
|
|
||||||
PostInit();
|
PostInit();
|
||||||
|
|
||||||
@ -23,10 +23,10 @@ Framework::Framework()
|
|||||||
|
|
||||||
Framework::~Framework()
|
Framework::~Framework()
|
||||||
{
|
{
|
||||||
delete restInterface;
|
delete httpServer;
|
||||||
delete log;
|
delete log;
|
||||||
|
|
||||||
restInterface = nullptr;
|
httpServer = nullptr;
|
||||||
log = nullptr;
|
log = nullptr;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -36,7 +36,7 @@ void Framework::Run()
|
|||||||
{
|
{
|
||||||
while (XGControl::keepServerRunning)
|
while (XGControl::keepServerRunning)
|
||||||
{
|
{
|
||||||
restInterface->Update();
|
httpServer->Update();
|
||||||
}
|
}
|
||||||
|
|
||||||
OnExit();
|
OnExit();
|
||||||
@ -50,6 +50,7 @@ void Framework::Run()
|
|||||||
void Framework::PreInit()
|
void Framework::PreInit()
|
||||||
{
|
{
|
||||||
LogHistory::PreInit();
|
LogHistory::PreInit();
|
||||||
|
XGConfig::PreInit();
|
||||||
RestQueryHandler::PreInit();
|
RestQueryHandler::PreInit();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -58,22 +59,23 @@ void Framework::PreInit()
|
|||||||
|
|
||||||
void Framework::PostInit()
|
void Framework::PostInit()
|
||||||
{
|
{
|
||||||
restInterface->PostInit();
|
httpServer->PostInit();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Framework::OnExit()
|
void Framework::OnExit()
|
||||||
{
|
{
|
||||||
restInterface->OnExit();
|
httpServer->OnExit();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Framework::PostExit()
|
void Framework::PostExit()
|
||||||
{
|
{
|
||||||
LogHistory::PostExit();
|
XGConfig::PostExit();
|
||||||
RestQueryHandler::PostExit();
|
RestQueryHandler::PostExit();
|
||||||
|
LogHistory::PostExit();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#include "LogHistory.h"
|
#include "LogHistory.h"
|
||||||
#include "RestInterface.h"
|
#include "HttpServer.h"
|
||||||
#include "XGControl.h"
|
#include "XGControl.h"
|
||||||
|
#include "XGConfig.h"
|
||||||
|
|
||||||
class Framework
|
class Framework
|
||||||
{
|
{
|
||||||
@ -18,7 +19,7 @@ private:
|
|||||||
void PreInit();
|
void PreInit();
|
||||||
void PostExit();
|
void PostExit();
|
||||||
|
|
||||||
Rest::RestInterface* restInterface;
|
Rest::HttpServer* httpServer;
|
||||||
|
|
||||||
Logging::Logger* log;
|
Logging::Logger* log;
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
#include "RestInterface.h"
|
#include "HttpServer.h"
|
||||||
|
|
||||||
using namespace Logging;
|
using namespace Logging;
|
||||||
using namespace Rest;
|
using namespace Rest;
|
||||||
using namespace JasonPP;
|
using namespace JasonPP;
|
||||||
|
|
||||||
RestInterface::RestInterface()
|
HttpServer::HttpServer()
|
||||||
{
|
{
|
||||||
pMgr = new mg_mgr();
|
pMgr = new mg_mgr();
|
||||||
pNc = nullptr;
|
pNc = nullptr;
|
||||||
@ -13,7 +13,7 @@ RestInterface::RestInterface()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
RestInterface::~RestInterface()
|
HttpServer::~HttpServer()
|
||||||
{
|
{
|
||||||
delete pMgr;
|
delete pMgr;
|
||||||
delete log;
|
delete log;
|
||||||
@ -24,31 +24,31 @@ RestInterface::~RestInterface()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RestInterface::PostInit()
|
void HttpServer::PostInit()
|
||||||
{
|
{
|
||||||
isBootedSuccessfully = InitWebServer();
|
isBootedSuccessfully = InitWebServer();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RestInterface::InitWebServer()
|
bool HttpServer::InitWebServer()
|
||||||
{
|
{
|
||||||
mg_mgr_init(pMgr, NULL);
|
mg_mgr_init(pMgr, NULL);
|
||||||
|
|
||||||
log->cout << "Starting rest api server on port " << WEBAPI_SERVER_PORT << "...";
|
log->cout << "Starting rest api server on port " << XGConfig::httpServer.port << "...";
|
||||||
log->Flush();
|
log->Flush();
|
||||||
|
|
||||||
pNc = mg_bind(pMgr, WEBAPI_SERVER_PORT, this->EventHandler);
|
pNc = mg_bind(pMgr, XGConfig::httpServer.port.c_str(), this->EventHandler);
|
||||||
|
|
||||||
if (pNc == NULL)
|
if (pNc == NULL)
|
||||||
{
|
{
|
||||||
log->cout << log->Err() << "Failed to boot rest api web server! - Unable to bind listener! (port: " << WEBAPI_SERVER_PORT << ")";
|
log->cout << log->Err() << "Failed to boot the http server! - Unable to bind listener! (port: " << XGConfig::httpServer.port << ")";
|
||||||
log->Flush();
|
log->Flush();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
mg_set_protocol_http_websocket(pNc);
|
mg_set_protocol_http_websocket(pNc);
|
||||||
frontend_serve_opts.document_root = "frontend";
|
frontend_serve_opts.document_root = XGConfig::httpServer.rootdir.c_str();
|
||||||
frontend_serve_opts.enable_directory_listing = "no";
|
frontend_serve_opts.enable_directory_listing = "no";
|
||||||
|
|
||||||
log->cout << "Started web server successfully!";
|
log->cout << "Started web server successfully!";
|
||||||
@ -58,14 +58,14 @@ bool RestInterface::InitWebServer()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RestInterface::Update()
|
void HttpServer::Update()
|
||||||
{
|
{
|
||||||
mg_mgr_poll(pMgr, WEBAPI_SERVER_POLLRATE);
|
mg_mgr_poll(pMgr, XGConfig::httpServer.pollingRate);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RestInterface::ServeStringToConnection(struct mg_connection* c, std::string str, int httpStatusCode)
|
void HttpServer::ServeStringToConnection(struct mg_connection* c, std::string str, int httpStatusCode)
|
||||||
{
|
{
|
||||||
mg_send_head(c, httpStatusCode, str.length(), "content-type: application/json\nAccess-Control-Allow-Origin: *");
|
mg_send_head(c, httpStatusCode, str.length(), "content-type: application/json\nAccess-Control-Allow-Origin: *");
|
||||||
mg_printf(c, str.c_str());
|
mg_printf(c, str.c_str());
|
||||||
@ -73,7 +73,7 @@ void RestInterface::ServeStringToConnection(struct mg_connection* c, std::string
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RestInterface::EventHandler(mg_connection* pNc, int ev, void* p)
|
void HttpServer::EventHandler(mg_connection* pNc, int ev, void* p)
|
||||||
{
|
{
|
||||||
switch (ev)
|
switch (ev)
|
||||||
{
|
{
|
||||||
@ -98,7 +98,7 @@ void RestInterface::EventHandler(mg_connection* pNc, int ev, void* p)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RestInterface::ProcessAPIRequest(mg_connection* pNc, int ev, void* p)
|
void HttpServer::ProcessAPIRequest(mg_connection* pNc, int ev, void* p)
|
||||||
{
|
{
|
||||||
// Get struct with http message informations
|
// Get struct with http message informations
|
||||||
http_message* hpm = (http_message*)p;
|
http_message* hpm = (http_message*)p;
|
||||||
@ -131,7 +131,7 @@ void RestInterface::ProcessAPIRequest(mg_connection* pNc, int ev, void* p)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RestInterface::OnExit()
|
void HttpServer::OnExit()
|
||||||
{
|
{
|
||||||
log->cout << "Shutting down rest api server...";
|
log->cout << "Shutting down rest api server...";
|
||||||
log->Flush();
|
log->Flush();
|
||||||
@ -142,7 +142,7 @@ void RestInterface::OnExit()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string RestInterface::FixUnterminatedString(const char* cstr, const std::size_t len)
|
std::string HttpServer::FixUnterminatedString(const char* cstr, const std::size_t len)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
for (std::size_t i = 0; i < len; i++)
|
for (std::size_t i = 0; i < len; i++)
|
||||||
@ -153,4 +153,4 @@ std::string RestInterface::FixUnterminatedString(const char* cstr, const std::si
|
|||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
mg_serve_http_opts RestInterface::frontend_serve_opts;
|
mg_serve_http_opts HttpServer::frontend_serve_opts;
|
@ -6,17 +6,15 @@
|
|||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#include "RestResponseTemplates.h"
|
#include "RestResponseTemplates.h"
|
||||||
#include "RestQueryHandler.h"
|
#include "RestQueryHandler.h"
|
||||||
|
#include "XGConfig.h"
|
||||||
#define WEBAPI_SERVER_POLLRATE 100
|
|
||||||
#define WEBAPI_SERVER_PORT "6969"
|
|
||||||
|
|
||||||
namespace Rest
|
namespace Rest
|
||||||
{
|
{
|
||||||
class RestInterface
|
class HttpServer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RestInterface();
|
HttpServer();
|
||||||
~RestInterface();
|
~HttpServer();
|
||||||
|
|
||||||
void PostInit();
|
void PostInit();
|
||||||
void Update();
|
void Update();
|
@ -1,25 +1,25 @@
|
|||||||
/*
|
/*
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
JasonPP, Copyright (c) 2020, Leon Etienne
|
JasonPP, Copyright (c) 2020, Leon Etienne
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
in the Software without restriction, including without limitation the rights
|
in the Software without restriction, including without limitation the rights
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
furnished to do so, subject to the following conditions:
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
The above copyright notice and this permission notice shall be included in all
|
||||||
copies or substantial portions of the Software.
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#include "JasonPP.hpp"
|
#include "JasonPP.hpp"
|
||||||
|
|
||||||
@ -262,7 +262,7 @@ std::size_t JsonArray::RemoveSimilar(const JsonData reference)
|
|||||||
return counter;
|
return counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t JsonArray::RemoveAllOfType(const JSON_DATA_TYPE type)
|
std::size_t JsonArray::RemoveAllOfType(const JDType type)
|
||||||
{
|
{
|
||||||
std::size_t counter = 0;
|
std::size_t counter = 0;
|
||||||
for (long long int i = content->size() - 1; i >= 0; i--)
|
for (long long int i = content->size() - 1; i >= 0; i--)
|
||||||
@ -277,7 +277,7 @@ std::size_t JsonArray::RemoveAllOfType(const JSON_DATA_TYPE type)
|
|||||||
return counter;
|
return counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t JsonArray::RemoveAllExceptType(const JSON_DATA_TYPE type)
|
std::size_t JsonArray::RemoveAllExceptType(const JDType type)
|
||||||
{
|
{
|
||||||
std::size_t counter = 0;
|
std::size_t counter = 0;
|
||||||
for (long long int i = content->size() - 1; i >= 0; i--)
|
for (long long int i = content->size() - 1; i >= 0; i--)
|
||||||
@ -524,7 +524,7 @@ void JsonArray::Sort(const std::string shorthandKey, const JSON_ARRAY_SORT_MODE
|
|||||||
const JsonData* b = &At(j + 1);
|
const JsonData* b = &At(j + 1);
|
||||||
|
|
||||||
// Check if they are of type json (this is the json sorter) (deep sort)
|
// Check if they are of type json (this is the json sorter) (deep sort)
|
||||||
if ((a->GetDataType() == JSON_DATA_TYPE::JSON) && (b->GetDataType() == JSON_DATA_TYPE::JSON))
|
if ((a->GetDataType() == JDType::JSON) && (b->GetDataType() == JDType::JSON))
|
||||||
{
|
{
|
||||||
// Check if the requested key even exists
|
// Check if the requested key even exists
|
||||||
if ((a->GetJsonData().DoesShorthandExist(shorthandKey, shorthandDelimiter)) &&
|
if ((a->GetJsonData().DoesShorthandExist(shorthandKey, shorthandDelimiter)) &&
|
||||||
@ -560,8 +560,8 @@ void JsonArray::Sort(const JSON_ARRAY_SORT_MODE mode)
|
|||||||
|
|
||||||
// Only if neither a or b's are neither of type json or array
|
// Only if neither a or b's are neither of type json or array
|
||||||
// This is the "shallow"-sort
|
// This is the "shallow"-sort
|
||||||
if (!(((a.GetDataType() == JSON_DATA_TYPE::JSON) || (a.GetDataType() == JSON_DATA_TYPE::ARRAY)) &&
|
if (!(((a.GetDataType() == JDType::JSON) || (a.GetDataType() == JDType::ARRAY)) &&
|
||||||
((b.GetDataType() == JSON_DATA_TYPE::JSON) || (b.GetDataType() == JSON_DATA_TYPE::ARRAY))))
|
((b.GetDataType() == JDType::JSON) || (b.GetDataType() == JDType::ARRAY))))
|
||||||
{
|
{
|
||||||
if (Sort__Compare(a, b, mode))
|
if (Sort__Compare(a, b, mode))
|
||||||
{
|
{
|
||||||
@ -590,11 +590,11 @@ bool JsonArray::Sort__Compare(const JsonData& a, const JsonData& b, const JSON_A
|
|||||||
// If it's BOOL, INT or FLOAT, just get it's value as string. Eg "53", "53.2" or "false"
|
// If it's BOOL, INT or FLOAT, just get it's value as string. Eg "53", "53.2" or "false"
|
||||||
// This way numerics can still be sorted alphabetically
|
// This way numerics can still be sorted alphabetically
|
||||||
// Also allows for sorting after bools
|
// Also allows for sorting after bools
|
||||||
if (a.GetDataType() == JSON_DATA_TYPE::STRING) aStr = a.GetStringData();
|
if (a.GetDataType() == JDType::STRING) aStr = a.GetStringData();
|
||||||
else if ((a.GetDataType() != JSON_DATA_TYPE::JSON) && (a.GetDataType() != JSON_DATA_TYPE::ARRAY)) aStr = a.Render();
|
else if ((a.GetDataType() != JDType::JSON) && (a.GetDataType() != JDType::ARRAY)) aStr = a.Render();
|
||||||
else return true; // Datatype invalid. Swap, to keep the others in order.
|
else return true; // Datatype invalid. Swap, to keep the others in order.
|
||||||
if (b.GetDataType() == JSON_DATA_TYPE::STRING) bStr = b.GetStringData();
|
if (b.GetDataType() == JDType::STRING) bStr = b.GetStringData();
|
||||||
else if ((b.GetDataType() != JSON_DATA_TYPE::JSON) && (b.GetDataType() != JSON_DATA_TYPE::ARRAY)) bStr = b.Render();
|
else if ((b.GetDataType() != JDType::JSON) && (b.GetDataType() != JDType::ARRAY)) bStr = b.Render();
|
||||||
else return true; // Datatype invalid. Swap, to keep the others in order.
|
else return true; // Datatype invalid. Swap, to keep the others in order.
|
||||||
|
|
||||||
return StringHelpers::SortDescriminator_Alphabetically(aStr, bStr);
|
return StringHelpers::SortDescriminator_Alphabetically(aStr, bStr);
|
||||||
@ -612,11 +612,11 @@ bool JsonArray::Sort__Compare(const JsonData& a, const JsonData& b, const JSON_A
|
|||||||
// If it's BOOL, INT or FLOAT, just get it's value as string. Eg "53", "53.2" or "false"
|
// If it's BOOL, INT or FLOAT, just get it's value as string. Eg "53", "53.2" or "false"
|
||||||
// This way numerics can still be sorted alphabetically
|
// This way numerics can still be sorted alphabetically
|
||||||
// Also allows for sorting after bools
|
// Also allows for sorting after bools
|
||||||
if (a.GetDataType() == JSON_DATA_TYPE::STRING) aStr = a.GetStringData();
|
if (a.GetDataType() == JDType::STRING) aStr = a.GetStringData();
|
||||||
else if ((a.GetDataType() != JSON_DATA_TYPE::JSON) && (a.GetDataType() != JSON_DATA_TYPE::ARRAY)) aStr = a.Render();
|
else if ((a.GetDataType() != JDType::JSON) && (a.GetDataType() != JDType::ARRAY)) aStr = a.Render();
|
||||||
else return true; // Datatype invalid. Swap, to keep the others in order.
|
else return true; // Datatype invalid. Swap, to keep the others in order.
|
||||||
if (b.GetDataType() == JSON_DATA_TYPE::STRING) bStr = b.GetStringData();
|
if (b.GetDataType() == JDType::STRING) bStr = b.GetStringData();
|
||||||
else if ((b.GetDataType() != JSON_DATA_TYPE::JSON) && (b.GetDataType() != JSON_DATA_TYPE::ARRAY)) bStr = b.Render();
|
else if ((b.GetDataType() != JDType::JSON) && (b.GetDataType() != JDType::ARRAY)) bStr = b.Render();
|
||||||
else return true; // Datatype invalid. Swap, to keep the others in order.
|
else return true; // Datatype invalid. Swap, to keep the others in order.
|
||||||
|
|
||||||
return StringHelpers::SortDescriminator_Alphabetically(bStr, aStr);
|
return StringHelpers::SortDescriminator_Alphabetically(bStr, aStr);
|
||||||
@ -631,13 +631,13 @@ bool JsonArray::Sort__Compare(const JsonData& a, const JsonData& b, const JSON_A
|
|||||||
|
|
||||||
switch (a.GetDataType())
|
switch (a.GetDataType())
|
||||||
{
|
{
|
||||||
case JSON_DATA_TYPE::INT:
|
case JDType::INT:
|
||||||
dataA = (long double)a.GetIntData();
|
dataA = (long double)a.GetIntData();
|
||||||
break;
|
break;
|
||||||
case JSON_DATA_TYPE::FLOAT:
|
case JDType::FLOAT:
|
||||||
dataA = a.GetFloatData();
|
dataA = a.GetFloatData();
|
||||||
break;
|
break;
|
||||||
case JSON_DATA_TYPE::BOOL:
|
case JDType::BOOL:
|
||||||
dataA = a.GetBoolData() ? 1.0 : 0.0;
|
dataA = a.GetBoolData() ? 1.0 : 0.0;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -645,13 +645,13 @@ bool JsonArray::Sort__Compare(const JsonData& a, const JsonData& b, const JSON_A
|
|||||||
}
|
}
|
||||||
switch (b.GetDataType())
|
switch (b.GetDataType())
|
||||||
{
|
{
|
||||||
case JSON_DATA_TYPE::INT:
|
case JDType::INT:
|
||||||
dataB = (long double)b.GetIntData();
|
dataB = (long double)b.GetIntData();
|
||||||
break;
|
break;
|
||||||
case JSON_DATA_TYPE::FLOAT:
|
case JDType::FLOAT:
|
||||||
dataB = b.GetFloatData();
|
dataB = b.GetFloatData();
|
||||||
break;
|
break;
|
||||||
case JSON_DATA_TYPE::BOOL:
|
case JDType::BOOL:
|
||||||
dataB = b.GetBoolData() ? 1.0 : 0.0;
|
dataB = b.GetBoolData() ? 1.0 : 0.0;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -669,13 +669,13 @@ bool JsonArray::Sort__Compare(const JsonData& a, const JsonData& b, const JSON_A
|
|||||||
|
|
||||||
switch (a.GetDataType())
|
switch (a.GetDataType())
|
||||||
{
|
{
|
||||||
case JSON_DATA_TYPE::INT:
|
case JDType::INT:
|
||||||
dataA = (long double)a.GetIntData();
|
dataA = (long double)a.GetIntData();
|
||||||
break;
|
break;
|
||||||
case JSON_DATA_TYPE::FLOAT:
|
case JDType::FLOAT:
|
||||||
dataA = a.GetFloatData();
|
dataA = a.GetFloatData();
|
||||||
break;
|
break;
|
||||||
case JSON_DATA_TYPE::BOOL:
|
case JDType::BOOL:
|
||||||
dataA = a.GetBoolData() ? 1.0 : 0.0;
|
dataA = a.GetBoolData() ? 1.0 : 0.0;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -683,13 +683,13 @@ bool JsonArray::Sort__Compare(const JsonData& a, const JsonData& b, const JSON_A
|
|||||||
}
|
}
|
||||||
switch (b.GetDataType())
|
switch (b.GetDataType())
|
||||||
{
|
{
|
||||||
case JSON_DATA_TYPE::INT:
|
case JDType::INT:
|
||||||
dataB = (long double)b.GetIntData();
|
dataB = (long double)b.GetIntData();
|
||||||
break;
|
break;
|
||||||
case JSON_DATA_TYPE::FLOAT:
|
case JDType::FLOAT:
|
||||||
dataB = b.GetFloatData();
|
dataB = b.GetFloatData();
|
||||||
break;
|
break;
|
||||||
case JSON_DATA_TYPE::BOOL:
|
case JDType::BOOL:
|
||||||
dataB = b.GetBoolData() ? 1.0 : 0.0;
|
dataB = b.GetBoolData() ? 1.0 : 0.0;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -753,7 +753,7 @@ JsonArray& JsonArray::operator-=(const JsonData& data)
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonArray& JsonArray::operator-=(const JSON_DATA_TYPE type)
|
JsonArray& JsonArray::operator-=(const JDType type)
|
||||||
{
|
{
|
||||||
RemoveAllOfType(type);
|
RemoveAllOfType(type);
|
||||||
return *this;
|
return *this;
|
||||||
@ -1031,29 +1031,29 @@ bool JasonPP::IsJsonValid(const std::string code)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string JasonPP::JsonDataType2String(const JSON_DATA_TYPE type)
|
std::string JasonPP::JsonDataType2String(const JDType type)
|
||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case JSON_DATA_TYPE::__NULL__:
|
case JDType::__NULL__:
|
||||||
return std::string("NULL");
|
return std::string("NULL");
|
||||||
|
|
||||||
case JSON_DATA_TYPE::BOOL:
|
case JDType::BOOL:
|
||||||
return std::string("BOOL");
|
return std::string("BOOL");
|
||||||
|
|
||||||
case JSON_DATA_TYPE::INT:
|
case JDType::INT:
|
||||||
return std::string("INT");
|
return std::string("INT");
|
||||||
|
|
||||||
case JSON_DATA_TYPE::FLOAT:
|
case JDType::FLOAT:
|
||||||
return std::string("FLOAT");
|
return std::string("FLOAT");
|
||||||
|
|
||||||
case JSON_DATA_TYPE::STRING:
|
case JDType::STRING:
|
||||||
return std::string("STRING");
|
return std::string("STRING");
|
||||||
|
|
||||||
case JSON_DATA_TYPE::JSON:
|
case JDType::JSON:
|
||||||
return std::string("JSON");
|
return std::string("JSON");
|
||||||
|
|
||||||
case JSON_DATA_TYPE::ARRAY:
|
case JDType::ARRAY:
|
||||||
return std::string("ARRAY");
|
return std::string("ARRAY");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1457,37 +1457,37 @@ JsonData::JsonData()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set default data per type
|
// Set default data per type
|
||||||
JsonData::JsonData(const JSON_DATA_TYPE type)
|
JsonData::JsonData(const JDType type)
|
||||||
{
|
{
|
||||||
Init();
|
Init();
|
||||||
|
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case JSON_DATA_TYPE::__NULL__:
|
case JDType::__NULL__:
|
||||||
// Default value is already NULL
|
// Default value is already NULL
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JSON_DATA_TYPE::BOOL:
|
case JDType::BOOL:
|
||||||
SetBoolData(false);
|
SetBoolData(false);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JSON_DATA_TYPE::INT:
|
case JDType::INT:
|
||||||
SetIntData(0);
|
SetIntData(0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JSON_DATA_TYPE::FLOAT:
|
case JDType::FLOAT:
|
||||||
SetFloatData(0);
|
SetFloatData(0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JSON_DATA_TYPE::STRING:
|
case JDType::STRING:
|
||||||
SetStringData("");
|
SetStringData("");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JSON_DATA_TYPE::JSON:
|
case JDType::JSON:
|
||||||
SetJsonDataAsPointer(new JsonBlock());
|
SetJsonDataAsPointer(new JsonBlock());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JSON_DATA_TYPE::ARRAY:
|
case JDType::ARRAY:
|
||||||
SetArrayDataAsPointer(new JsonArray());
|
SetArrayDataAsPointer(new JsonArray());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1602,7 +1602,7 @@ void JsonData::SetFloatPrecision(const double precision)
|
|||||||
|
|
||||||
JsonArray& JsonData::SetArrayDataAsPointer(JsonArray* p)
|
JsonArray& JsonData::SetArrayDataAsPointer(JsonArray* p)
|
||||||
{
|
{
|
||||||
dataType = JSON_DATA_TYPE::ARRAY;
|
dataType = JDType::ARRAY;
|
||||||
|
|
||||||
if (arrayData != nullptr)
|
if (arrayData != nullptr)
|
||||||
{
|
{
|
||||||
@ -1617,7 +1617,7 @@ JsonArray& JsonData::SetArrayDataAsPointer(JsonArray* p)
|
|||||||
|
|
||||||
JsonBlock& JsonData::SetJsonDataAsPointer(JsonBlock* p)
|
JsonBlock& JsonData::SetJsonDataAsPointer(JsonBlock* p)
|
||||||
{
|
{
|
||||||
dataType = JSON_DATA_TYPE::JSON;
|
dataType = JDType::JSON;
|
||||||
|
|
||||||
if (jsonData != nullptr)
|
if (jsonData != nullptr)
|
||||||
{
|
{
|
||||||
@ -1631,7 +1631,7 @@ JsonBlock& JsonData::SetJsonDataAsPointer(JsonBlock* p)
|
|||||||
|
|
||||||
void JsonData::Init()
|
void JsonData::Init()
|
||||||
{
|
{
|
||||||
dataType = JSON_DATA_TYPE::__NULL__;
|
dataType = JDType::__NULL__;
|
||||||
intData = 0l;
|
intData = 0l;
|
||||||
floatData = 0.0f;
|
floatData = 0.0f;
|
||||||
stringData = std::string();
|
stringData = std::string();
|
||||||
@ -1686,12 +1686,12 @@ double JsonData::GetFloatPrecision() const
|
|||||||
|
|
||||||
bool JsonData::IsOfNumericType() const
|
bool JsonData::IsOfNumericType() const
|
||||||
{
|
{
|
||||||
return (dataType == JSON_DATA_TYPE::INT) || (dataType == JSON_DATA_TYPE::FLOAT);
|
return (dataType == JDType::INT) || (dataType == JDType::FLOAT);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool JsonData::GetBoolData() const
|
bool JsonData::GetBoolData() const
|
||||||
{
|
{
|
||||||
JSON_DATA_TYPE typeToGet = JSON_DATA_TYPE::BOOL;
|
JDType typeToGet = JDType::BOOL;
|
||||||
|
|
||||||
if (dataType != typeToGet) ThrowDataTypeException(typeToGet);
|
if (dataType != typeToGet) ThrowDataTypeException(typeToGet);
|
||||||
|
|
||||||
@ -1700,11 +1700,11 @@ bool JsonData::GetBoolData() const
|
|||||||
|
|
||||||
long long int JsonData::GetIntData() const
|
long long int JsonData::GetIntData() const
|
||||||
{
|
{
|
||||||
JSON_DATA_TYPE typeToGet = JSON_DATA_TYPE::INT;
|
JDType typeToGet = JDType::INT;
|
||||||
|
|
||||||
if (dataType != typeToGet)
|
if (dataType != typeToGet)
|
||||||
{
|
{
|
||||||
if (dataType == JSON_DATA_TYPE::FLOAT)
|
if (dataType == JDType::FLOAT)
|
||||||
{
|
{
|
||||||
return (long long int)floatData;
|
return (long long int)floatData;
|
||||||
}
|
}
|
||||||
@ -1719,11 +1719,11 @@ long long int JsonData::GetIntData() const
|
|||||||
|
|
||||||
long double JsonData::GetFloatData() const
|
long double JsonData::GetFloatData() const
|
||||||
{
|
{
|
||||||
JSON_DATA_TYPE typeToGet = JSON_DATA_TYPE::FLOAT;
|
JDType typeToGet = JDType::FLOAT;
|
||||||
|
|
||||||
if (dataType != typeToGet)
|
if (dataType != typeToGet)
|
||||||
{
|
{
|
||||||
if (dataType == JSON_DATA_TYPE::INT)
|
if (dataType == JDType::INT)
|
||||||
{
|
{
|
||||||
return (float)intData;
|
return (float)intData;
|
||||||
}
|
}
|
||||||
@ -1738,7 +1738,7 @@ long double JsonData::GetFloatData() const
|
|||||||
|
|
||||||
std::string JsonData::GetStringData() const
|
std::string JsonData::GetStringData() const
|
||||||
{
|
{
|
||||||
JSON_DATA_TYPE typeToGet = JSON_DATA_TYPE::STRING;
|
JDType typeToGet = JDType::STRING;
|
||||||
|
|
||||||
if (dataType != typeToGet) ThrowDataTypeException(typeToGet);
|
if (dataType != typeToGet) ThrowDataTypeException(typeToGet);
|
||||||
|
|
||||||
@ -1747,35 +1747,35 @@ std::string JsonData::GetStringData() const
|
|||||||
|
|
||||||
JsonBlock& JsonData::GetJsonData()
|
JsonBlock& JsonData::GetJsonData()
|
||||||
{
|
{
|
||||||
JSON_DATA_TYPE typeToGet = JSON_DATA_TYPE::JSON;
|
JDType typeToGet = JDType::JSON;
|
||||||
if (dataType != typeToGet) ThrowDataTypeException(typeToGet);
|
if (dataType != typeToGet) ThrowDataTypeException(typeToGet);
|
||||||
return *jsonData;
|
return *jsonData;
|
||||||
}
|
}
|
||||||
|
|
||||||
const JsonBlock& JsonData::GetJsonData() const
|
const JsonBlock& JsonData::GetJsonData() const
|
||||||
{
|
{
|
||||||
JSON_DATA_TYPE typeToGet = JSON_DATA_TYPE::JSON;
|
JDType typeToGet = JDType::JSON;
|
||||||
if (dataType != typeToGet) ThrowDataTypeException(typeToGet);
|
if (dataType != typeToGet) ThrowDataTypeException(typeToGet);
|
||||||
return *jsonData;
|
return *jsonData;
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonArray& JsonData::GetArrayData()
|
JsonArray& JsonData::GetArrayData()
|
||||||
{
|
{
|
||||||
JSON_DATA_TYPE typeToGet = JSON_DATA_TYPE::ARRAY;
|
JDType typeToGet = JDType::ARRAY;
|
||||||
if (dataType != typeToGet) ThrowDataTypeException(typeToGet);
|
if (dataType != typeToGet) ThrowDataTypeException(typeToGet);
|
||||||
return *arrayData;
|
return *arrayData;
|
||||||
}
|
}
|
||||||
|
|
||||||
const JsonArray& JsonData::GetArrayData() const
|
const JsonArray& JsonData::GetArrayData() const
|
||||||
{
|
{
|
||||||
JSON_DATA_TYPE typeToGet = JSON_DATA_TYPE::ARRAY;
|
JDType typeToGet = JDType::ARRAY;
|
||||||
if (dataType != typeToGet) ThrowDataTypeException(typeToGet);
|
if (dataType != typeToGet) ThrowDataTypeException(typeToGet);
|
||||||
return *arrayData;
|
return *arrayData;
|
||||||
}
|
}
|
||||||
|
|
||||||
short JsonData::GetNullData()
|
short JsonData::GetNullData()
|
||||||
{
|
{
|
||||||
JSON_DATA_TYPE typeToGet = JSON_DATA_TYPE::__NULL__;
|
JDType typeToGet = JDType::__NULL__;
|
||||||
|
|
||||||
if (dataType != typeToGet) ThrowDataTypeException(typeToGet);
|
if (dataType != typeToGet) ThrowDataTypeException(typeToGet);
|
||||||
|
|
||||||
@ -1803,7 +1803,7 @@ bool JsonData::HasParent() const
|
|||||||
|
|
||||||
void JsonData::SetBoolData(const bool data)
|
void JsonData::SetBoolData(const bool data)
|
||||||
{
|
{
|
||||||
dataType = JSON_DATA_TYPE::BOOL;
|
dataType = JDType::BOOL;
|
||||||
intData = (int)data;
|
intData = (int)data;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -1811,7 +1811,7 @@ void JsonData::SetBoolData(const bool data)
|
|||||||
|
|
||||||
void JsonData::SetIntData(const long long int data)
|
void JsonData::SetIntData(const long long int data)
|
||||||
{
|
{
|
||||||
dataType = JSON_DATA_TYPE::INT;
|
dataType = JDType::INT;
|
||||||
intData = data;
|
intData = data;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -1819,7 +1819,7 @@ void JsonData::SetIntData(const long long int data)
|
|||||||
|
|
||||||
void JsonData::SetIntData(const int data)
|
void JsonData::SetIntData(const int data)
|
||||||
{
|
{
|
||||||
dataType = JSON_DATA_TYPE::INT;
|
dataType = JDType::INT;
|
||||||
intData = data;
|
intData = data;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -1827,7 +1827,7 @@ void JsonData::SetIntData(const int data)
|
|||||||
|
|
||||||
void JsonData::SetFloatData(const long double data)
|
void JsonData::SetFloatData(const long double data)
|
||||||
{
|
{
|
||||||
dataType = JSON_DATA_TYPE::FLOAT;
|
dataType = JDType::FLOAT;
|
||||||
floatData = data;
|
floatData = data;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -1835,7 +1835,7 @@ void JsonData::SetFloatData(const long double data)
|
|||||||
|
|
||||||
void JsonData::SetStringData(const std::string data)
|
void JsonData::SetStringData(const std::string data)
|
||||||
{
|
{
|
||||||
dataType = JSON_DATA_TYPE::STRING;
|
dataType = JDType::STRING;
|
||||||
stringData = data;
|
stringData = data;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -1848,7 +1848,7 @@ JsonBlock& JsonData::SetJsonData(const JsonBlock data)
|
|||||||
|
|
||||||
JsonArray& JsonData::SetArrayData(const std::vector<JsonData> data)
|
JsonArray& JsonData::SetArrayData(const std::vector<JsonData> data)
|
||||||
{
|
{
|
||||||
dataType = JSON_DATA_TYPE::ARRAY;
|
dataType = JDType::ARRAY;
|
||||||
JsonArray* newArr = new JsonArray;
|
JsonArray* newArr = new JsonArray;
|
||||||
newArr->CopyJsonDataFromVector_Pointer(&data); // Slightly more performant than constructor
|
newArr->CopyJsonDataFromVector_Pointer(&data); // Slightly more performant than constructor
|
||||||
return SetArrayDataAsPointer(newArr);
|
return SetArrayDataAsPointer(newArr);
|
||||||
@ -1861,14 +1861,14 @@ JsonArray& JsonData::SetArrayData(const JsonArray data)
|
|||||||
|
|
||||||
void JsonData::SetNull()
|
void JsonData::SetNull()
|
||||||
{
|
{
|
||||||
dataType = JSON_DATA_TYPE::__NULL__;
|
dataType = JDType::__NULL__;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* MISC */
|
/* MISC */
|
||||||
|
|
||||||
void JsonData::ThrowDataTypeException(const JSON_DATA_TYPE toFetch) const
|
void JsonData::ThrowDataTypeException(const JDType toFetch) const
|
||||||
{
|
{
|
||||||
throw JsonWrongDataTypeException(
|
throw JsonWrongDataTypeException(
|
||||||
JsonDataType2String(toFetch),
|
JsonDataType2String(toFetch),
|
||||||
@ -1890,32 +1890,32 @@ std::string JsonData::Render(unsigned int num_tabs, const bool minify) const
|
|||||||
|
|
||||||
switch (dataType)
|
switch (dataType)
|
||||||
{
|
{
|
||||||
case JSON_DATA_TYPE::__NULL__:
|
case JDType::__NULL__:
|
||||||
ss << "null";
|
ss << "null";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JSON_DATA_TYPE::BOOL:
|
case JDType::BOOL:
|
||||||
ss << ((intData != 0) ? "true" : "false");
|
ss << ((intData != 0) ? "true" : "false");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JSON_DATA_TYPE::INT:
|
case JDType::INT:
|
||||||
ss << intData;
|
ss << intData;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JSON_DATA_TYPE::FLOAT:
|
case JDType::FLOAT:
|
||||||
ss.precision((std::streamsize)((-log10(GetFloatPrecision())) + 1));
|
ss.precision((std::streamsize)((-log10(GetFloatPrecision())) + 1));
|
||||||
ss << floatData;
|
ss << floatData;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JSON_DATA_TYPE::STRING:
|
case JDType::STRING:
|
||||||
ss << "\"" << StringHelpers::Escape(stringData) << "\"";
|
ss << "\"" << StringHelpers::Escape(stringData) << "\"";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JSON_DATA_TYPE::JSON:
|
case JDType::JSON:
|
||||||
ss << jsonData->Render(num_tabs, minify);
|
ss << jsonData->Render(num_tabs, minify);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JSON_DATA_TYPE::ARRAY:
|
case JDType::ARRAY:
|
||||||
ss << arrayData->Render(num_tabs, minify);
|
ss << arrayData->Render(num_tabs, minify);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2007,31 +2007,31 @@ void JsonData::CloneFrom(const JsonData& other)
|
|||||||
|
|
||||||
switch (other.dataType)
|
switch (other.dataType)
|
||||||
{
|
{
|
||||||
case JSON_DATA_TYPE::__NULL__:
|
case JDType::__NULL__:
|
||||||
// Default value is already NULL
|
// Default value is already NULL
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JSON_DATA_TYPE::BOOL:
|
case JDType::BOOL:
|
||||||
SetBoolData(other.intData != 0);
|
SetBoolData(other.intData != 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JSON_DATA_TYPE::INT:
|
case JDType::INT:
|
||||||
SetIntData(other.intData);
|
SetIntData(other.intData);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JSON_DATA_TYPE::FLOAT:
|
case JDType::FLOAT:
|
||||||
SetFloatData(other.floatData);
|
SetFloatData(other.floatData);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JSON_DATA_TYPE::STRING:
|
case JDType::STRING:
|
||||||
SetStringData(other.stringData);
|
SetStringData(other.stringData);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JSON_DATA_TYPE::ARRAY:
|
case JDType::ARRAY:
|
||||||
SetArrayData(*other.arrayData);
|
SetArrayData(*other.arrayData);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JSON_DATA_TYPE::JSON:
|
case JDType::JSON:
|
||||||
SetJsonData(*other.jsonData);
|
SetJsonData(*other.jsonData);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2042,8 +2042,8 @@ void JsonData::CloneFrom(const JsonData& other)
|
|||||||
bool JsonData::IsIdentical(const JsonData& other) const
|
bool JsonData::IsIdentical(const JsonData& other) const
|
||||||
{
|
{
|
||||||
// Special case for int/float implicit conversion
|
// Special case for int/float implicit conversion
|
||||||
if (((dataType == JSON_DATA_TYPE::INT) && (other.dataType == JSON_DATA_TYPE::FLOAT)) ||
|
if (((dataType == JDType::INT) && (other.dataType == JDType::FLOAT)) ||
|
||||||
((other.dataType == JSON_DATA_TYPE::INT) && (dataType == JSON_DATA_TYPE::FLOAT)))
|
((other.dataType == JDType::INT) && (dataType == JDType::FLOAT)))
|
||||||
{
|
{
|
||||||
// Here we have to get the float value via the getter because of implicit conversion
|
// Here we have to get the float value via the getter because of implicit conversion
|
||||||
// Use the more precise precision of the two...
|
// Use the more precise precision of the two...
|
||||||
@ -2055,32 +2055,32 @@ bool JsonData::IsIdentical(const JsonData& other) const
|
|||||||
|
|
||||||
switch (dataType)
|
switch (dataType)
|
||||||
{
|
{
|
||||||
case JSON_DATA_TYPE::__NULL__:
|
case JDType::__NULL__:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case JSON_DATA_TYPE::BOOL:
|
case JDType::BOOL:
|
||||||
// Values can't be of different type because of the check at the beginning of the function
|
// Values can't be of different type because of the check at the beginning of the function
|
||||||
return intData == other.intData;
|
return intData == other.intData;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JSON_DATA_TYPE::INT:
|
case JDType::INT:
|
||||||
return intData == other.intData;
|
return intData == other.intData;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JSON_DATA_TYPE::FLOAT:
|
case JDType::FLOAT:
|
||||||
// Use the more precise precision of the two...
|
// Use the more precise precision of the two...
|
||||||
return Helpers::AreSame(floatData, other.floatData, Helpers::Min<double>(GetFloatPrecision(), other.GetFloatPrecision()));
|
return Helpers::AreSame(floatData, other.floatData, Helpers::Min<double>(GetFloatPrecision(), other.GetFloatPrecision()));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JSON_DATA_TYPE::STRING:
|
case JDType::STRING:
|
||||||
return stringData == other.stringData;
|
return stringData == other.stringData;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JSON_DATA_TYPE::ARRAY:
|
case JDType::ARRAY:
|
||||||
return arrayData->IsIdentical(*other.arrayData);
|
return arrayData->IsIdentical(*other.arrayData);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JSON_DATA_TYPE::JSON:
|
case JDType::JSON:
|
||||||
return jsonData->IsIdentical(*other.jsonData);
|
return jsonData->IsIdentical(*other.jsonData);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2140,11 +2140,11 @@ bool JsonData::operator!=(const JsonData& other) const
|
|||||||
|
|
||||||
JsonData& JsonData::operator+=(const JsonElement ele)
|
JsonData& JsonData::operator+=(const JsonElement ele)
|
||||||
{
|
{
|
||||||
if (dataType == JSON_DATA_TYPE::JSON)
|
if (dataType == JDType::JSON)
|
||||||
{
|
{
|
||||||
return jsonData->Add(ele);
|
return jsonData->Add(ele);
|
||||||
}
|
}
|
||||||
ThrowDataTypeException(JSON_DATA_TYPE::JSON);
|
ThrowDataTypeException(JDType::JSON);
|
||||||
std::terminate();
|
std::terminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2257,7 +2257,7 @@ JsonData::operator long double() const
|
|||||||
|
|
||||||
JsonData::operator std::string() const
|
JsonData::operator std::string() const
|
||||||
{
|
{
|
||||||
if (dataType == JSON_DATA_TYPE::STRING) return GetStringData();
|
if (dataType == JDType::STRING) return GetStringData();
|
||||||
else return Render(JASONPP_STRINGCONV_MINIFY);
|
else return Render(JASONPP_STRINGCONV_MINIFY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2266,7 +2266,7 @@ namespace JasonPP
|
|||||||
{
|
{
|
||||||
std::ostream& operator<<(std::ostream& os, const JsonData& jd)
|
std::ostream& operator<<(std::ostream& os, const JsonData& jd)
|
||||||
{
|
{
|
||||||
if (jd.dataType == JSON_DATA_TYPE::STRING) return os << jd.GetStringData();
|
if (jd.dataType == JDType::STRING) return os << jd.GetStringData();
|
||||||
else return os << jd.Render(JASONPP_STRINGCONV_MINIFY);
|
else return os << jd.Render(JASONPP_STRINGCONV_MINIFY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2690,10 +2690,10 @@ bool JsonBlock::DoesShorthandExist(const std::string shorthand, const std::strin
|
|||||||
const JsonBlock* jb = &const_cast<const JsonBlock&>(*this);
|
const JsonBlock* jb = &const_cast<const JsonBlock&>(*this);
|
||||||
for (std::size_t i = 0; i < segments.size(); i++)
|
for (std::size_t i = 0; i < segments.size(); i++)
|
||||||
{
|
{
|
||||||
if ((jb->DoesExist(segments[i])) && ((jb->Get(segments[i]).GetDataType() == JSON_DATA_TYPE::JSON) || (i == segments.size() - 1)))
|
if ((jb->DoesExist(segments[i])) && ((jb->Get(segments[i]).GetDataType() == JDType::JSON) || (i == segments.size() - 1)))
|
||||||
{
|
{
|
||||||
if (i == segments.size() - 1) return true; // We are at the end. Let's just return it
|
if (i == segments.size() - 1) return true; // We are at the end. Let's just return it
|
||||||
if (jb->Get(segments[i]).GetDataType() == JSON_DATA_TYPE::JSON) jb = &jb->Get(segments[i]).GetJsonData();
|
if (jb->Get(segments[i]).GetDataType() == JDType::JSON) jb = &jb->Get(segments[i]).GetJsonData();
|
||||||
else return false;
|
else return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -2718,10 +2718,10 @@ const JsonData& JsonBlock::ShorthandGet(const std::string shorthand, const std::
|
|||||||
const JsonBlock* jb = &const_cast<const JsonBlock&>(*this);
|
const JsonBlock* jb = &const_cast<const JsonBlock&>(*this);
|
||||||
for (std::size_t i = 0; i < segments.size(); i++)
|
for (std::size_t i = 0; i < segments.size(); i++)
|
||||||
{
|
{
|
||||||
if ((jb->DoesExist(segments[i])) && ((jb->Get(segments[i]).GetDataType() == JSON_DATA_TYPE::JSON) || (i == segments.size() - 1)))
|
if ((jb->DoesExist(segments[i])) && ((jb->Get(segments[i]).GetDataType() == JDType::JSON) || (i == segments.size() - 1)))
|
||||||
{
|
{
|
||||||
if (i == segments.size() - 1) return jb->Get(segments[i]); // We are at the end. Let's just return it
|
if (i == segments.size() - 1) return jb->Get(segments[i]); // We are at the end. Let's just return it
|
||||||
if (jb->Get(segments[i]).GetDataType() == JSON_DATA_TYPE::JSON) jb = &jb->Get(segments[i]).GetJsonData();
|
if (jb->Get(segments[i]).GetDataType() == JDType::JSON) jb = &jb->Get(segments[i]).GetJsonData();
|
||||||
else throw JsonShorthandInvalidException(shorthand);
|
else throw JsonShorthandInvalidException(shorthand);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -2757,7 +2757,7 @@ JsonData& JsonBlock::ShorthandAdd(const std::string shorthand, const std::string
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (jb->Get(segments[i]).GetDataType() != JSON_DATA_TYPE::JSON) throw JsonShorthandInvalidException(shorthand, "A path segment already exists and is not of type json!");
|
if (jb->Get(segments[i]).GetDataType() != JDType::JSON) throw JsonShorthandInvalidException(shorthand, "A path segment already exists and is not of type json!");
|
||||||
jb = &jb->Get(segments[i]).GetJsonData();
|
jb = &jb->Get(segments[i]).GetJsonData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2804,7 +2804,7 @@ void JsonBlock::ShorthandRemove(const std::string shorthand, const std::string d
|
|||||||
JsonData* dt = &ShorthandGet(shorthandParent, delimiter);
|
JsonData* dt = &ShorthandGet(shorthandParent, delimiter);
|
||||||
|
|
||||||
// Is the parent object of the object to be deleted even of type json?
|
// Is the parent object of the object to be deleted even of type json?
|
||||||
if (dt->GetDataType() != JSON_DATA_TYPE::JSON) throw JsonShorthandInvalidException(shorthand, "The parent of the object to be deleted is not of type json!");
|
if (dt->GetDataType() != JDType::JSON) throw JsonShorthandInvalidException(shorthand, "The parent of the object to be deleted is not of type json!");
|
||||||
parentJson = &dt->GetJsonData();
|
parentJson = &dt->GetJsonData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2142,13 +2142,18 @@ namespace JasonPP
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#define JASONPP_VERSION (1.02)
|
#define JASONPP_VERSION (1.021)
|
||||||
|
|
||||||
namespace JasonPP
|
namespace JasonPP
|
||||||
{
|
{
|
||||||
typedef JsonData Json;
|
typedef JsonData Json;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace JasonPP
|
||||||
|
{
|
||||||
|
typedef JSON_DATA_TYPE JDType;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
namespace JasonPP
|
namespace JasonPP
|
||||||
{
|
{
|
||||||
@ -2198,8 +2203,8 @@ namespace JasonPP
|
|||||||
#define AsJson GetJsonData()
|
#define AsJson GetJsonData()
|
||||||
#define AsArray GetArrayData()
|
#define AsArray GetArrayData()
|
||||||
|
|
||||||
#define Arr std::vector<JasonPP::JsonData>
|
#define Arr std::vector<::JasonPP::JsonData>
|
||||||
#define Ele JasonPP::JsonElement
|
#define Ele ::JasonPP::JsonElement
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -11,7 +11,10 @@ void LogHistory::PreInit()
|
|||||||
|
|
||||||
void LogHistory::PostExit()
|
void LogHistory::PostExit()
|
||||||
{
|
{
|
||||||
for (unsigned int i = 0; i < history->size(); i++)
|
Save();
|
||||||
|
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < history->size(); i++)
|
||||||
{
|
{
|
||||||
delete history->at(i);
|
delete history->at(i);
|
||||||
history->at(i) = nullptr;
|
history->at(i) = nullptr;
|
||||||
@ -23,6 +26,29 @@ void LogHistory::PostExit()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LogHistory::Save()
|
||||||
|
{
|
||||||
|
std::stringstream textfile;
|
||||||
|
JasonPP::Json jsonFile = JasonPP::JsonArray();
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < history->size(); i++)
|
||||||
|
{
|
||||||
|
textfile << history->at(i)->compiledMessage << std::endl;
|
||||||
|
jsonFile.AsArray += history->at(i)->GetAsJson();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ofstream ofs;
|
||||||
|
ofs.open(XGConfig::logging.logfile_text, std::ios::app);
|
||||||
|
ofs << textfile.str();
|
||||||
|
ofs.close();
|
||||||
|
|
||||||
|
ofs.open(XGConfig::logging.logfile_json, std::ios::app);
|
||||||
|
ofs << jsonFile.Render();
|
||||||
|
ofs.close();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
void LogHistory::AddLogToHistory(LogEntry* newEntry)
|
void LogHistory::AddLogToHistory(LogEntry* newEntry)
|
||||||
{
|
{
|
||||||
history->push_back(newEntry);
|
history->push_back(newEntry);
|
||||||
@ -31,3 +57,14 @@ void LogHistory::AddLogToHistory(LogEntry* newEntry)
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<LogEntry*>* LogHistory::history;
|
std::vector<LogEntry*>* LogHistory::history;
|
||||||
|
|
||||||
|
JasonPP::JsonBlock LogEntry::GetAsJson()
|
||||||
|
{
|
||||||
|
return JasonPP::JsonBlock({
|
||||||
|
Ele("compiledMessage", message),
|
||||||
|
Ele("message", compiledMessage),
|
||||||
|
Ele("identifier", identifier),
|
||||||
|
Ele("type", (int)type),
|
||||||
|
Ele("timestamp", (long long int)timestamp),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -2,7 +2,11 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
#include <sstream>
|
||||||
|
#include <fstream>
|
||||||
#include "LogTypes.h"
|
#include "LogTypes.h"
|
||||||
|
#include "XGConfig.h"
|
||||||
|
#include "JasonPP.hpp"
|
||||||
|
|
||||||
namespace Logging
|
namespace Logging
|
||||||
{
|
{
|
||||||
@ -11,10 +15,13 @@ namespace Logging
|
|||||||
class LogEntry
|
class LogEntry
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
std::string compiledMessage;
|
||||||
std::string message;
|
std::string message;
|
||||||
std::string identifier;
|
std::string identifier;
|
||||||
LOG_TYPE type;
|
LOG_TYPE type;
|
||||||
std::time_t timestamp;
|
std::time_t timestamp;
|
||||||
|
|
||||||
|
JasonPP::JsonBlock GetAsJson();
|
||||||
};
|
};
|
||||||
|
|
||||||
class LogHistory
|
class LogHistory
|
||||||
@ -23,6 +30,8 @@ namespace Logging
|
|||||||
static void PreInit();
|
static void PreInit();
|
||||||
static void PostExit();
|
static void PostExit();
|
||||||
|
|
||||||
|
static void Save();
|
||||||
|
|
||||||
static std::vector<LogEntry*>* GetLogHistory() { return history; }
|
static std::vector<LogEntry*>* GetLogHistory() { return history; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace Logging
|
namespace Logging
|
||||||
{
|
{
|
||||||
enum class LOG_TYPE
|
enum LOG_TYPE
|
||||||
{
|
{
|
||||||
LOG,
|
LOG,
|
||||||
WARN,
|
WARN,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
|
#include "LogHistory.h"
|
||||||
|
|
||||||
using namespace Logging;
|
using namespace Logging;
|
||||||
|
|
||||||
@ -48,7 +49,8 @@ std::string Logger::Flush()
|
|||||||
bufOut << "<" << timeBuf << "> [" << identifier << "]" << TypeToPrefix(type) << ((additionalInfo.length() > 0) ? " " : "") << additionalInfo << ": " << cout.str();
|
bufOut << "<" << timeBuf << "> [" << identifier << "]" << TypeToPrefix(type) << ((additionalInfo.length() > 0) ? " " : "") << additionalInfo << ": " << cout.str();
|
||||||
|
|
||||||
LogEntry* newEntry = new LogEntry;
|
LogEntry* newEntry = new LogEntry;
|
||||||
newEntry->message = bufOut.str();
|
newEntry->message = cout.str();
|
||||||
|
newEntry->compiledMessage = bufOut.str();
|
||||||
newEntry->identifier = identifier;
|
newEntry->identifier = identifier;
|
||||||
newEntry->timestamp = currTime;
|
newEntry->timestamp = currTime;
|
||||||
newEntry->type = type;
|
newEntry->type = type;
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include "LogTypes.h"
|
#include "LogTypes.h"
|
||||||
#include "LogHistory.h"
|
|
||||||
|
|
||||||
namespace Logging
|
namespace Logging
|
||||||
{
|
{
|
||||||
|
@ -15,7 +15,7 @@ bool RestQueryHandler::ProcessQuery(const std::string clientAdress, const Json&
|
|||||||
{
|
{
|
||||||
log->SetAdditionalInformation(std::string("@") + clientAdress);
|
log->SetAdditionalInformation(std::string("@") + clientAdress);
|
||||||
|
|
||||||
if (!ValidateField("request", JSON_DATA_TYPE::STRING, request, responseBody))
|
if (!ValidateField("request", JDType::STRING, request, responseBody))
|
||||||
{
|
{
|
||||||
responseCode = BAD_REQUEST;
|
responseCode = BAD_REQUEST;
|
||||||
return false;
|
return false;
|
||||||
@ -23,7 +23,10 @@ bool RestQueryHandler::ProcessQuery(const std::string clientAdress, const Json&
|
|||||||
JsonBlock requestBody = request.AsJson;
|
JsonBlock requestBody = request.AsJson;
|
||||||
std::string requestName = requestBody.Get("request").AsString;
|
std::string requestName = requestBody.Get("request").AsString;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (requestName == "kill_yourself") return KillYourself(requestBody, responseBody, responseCode);
|
if (requestName == "kill_yourself") return KillYourself(requestBody, responseBody, responseCode);
|
||||||
|
else if (requestName == "foo") return Example_Foo(requestBody, responseBody, responseCode);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -32,7 +35,7 @@ bool RestQueryHandler::ProcessQuery(const std::string clientAdress, const Json&
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rest::RestQueryHandler::PostExit()
|
void RestQueryHandler::PostExit()
|
||||||
{
|
{
|
||||||
delete log;
|
delete log;
|
||||||
log = nullptr;
|
log = nullptr;
|
||||||
@ -40,6 +43,15 @@ void Rest::RestQueryHandler::PostExit()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RestQueryHandler::Example_Foo(const JsonBlock& request, JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode)
|
||||||
|
{
|
||||||
|
responseCode = OK;
|
||||||
|
responseBody.CloneFrom(RestResponseTemplates::GetByCode(OK));
|
||||||
|
responseBody.Set("message") = "Bar!";
|
||||||
|
std::cout << "Bar!" << std::endl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool RestQueryHandler::KillYourself(const JsonBlock& request, JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode)
|
bool RestQueryHandler::KillYourself(const JsonBlock& request, JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode)
|
||||||
{
|
{
|
||||||
XGControl::keepServerRunning = false;
|
XGControl::keepServerRunning = false;
|
||||||
@ -53,9 +65,9 @@ bool RestQueryHandler::KillYourself(const JsonBlock& request, JsonBlock& respons
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RestQueryHandler::ValidateField(const std::string name, const JasonPP::JSON_DATA_TYPE type, const JasonPP::Json& checkThat, JasonPP::JsonBlock& putErrorResponseHere)
|
bool RestQueryHandler::ValidateField(const std::string name, const JasonPP::JDType type, const JasonPP::Json& checkThat, JasonPP::JsonBlock& putErrorResponseHere)
|
||||||
{
|
{
|
||||||
if (checkThat.GetDataType() != JSON_DATA_TYPE::JSON)
|
if (checkThat.GetDataType() != JDType::JSON)
|
||||||
{
|
{
|
||||||
putErrorResponseHere = RestResponseTemplates::GetByCode(BAD_REQUEST, "The request body must be a json struct! No json array or similar...");
|
putErrorResponseHere = RestResponseTemplates::GetByCode(BAD_REQUEST, "The request body must be a json struct! No json array or similar...");
|
||||||
return false;
|
return false;
|
||||||
@ -68,8 +80,8 @@ bool RestQueryHandler::ValidateField(const std::string name, const JasonPP::JSON
|
|||||||
const JsonData& cached = cachedJson.ShorthandGet(name);
|
const JsonData& cached = cachedJson.ShorthandGet(name);
|
||||||
|
|
||||||
if ((cached.GetDataType() == type) ||
|
if ((cached.GetDataType() == type) ||
|
||||||
((cached.IsOfNumericType()) && (type == JSON_DATA_TYPE::INT)) ||
|
((cached.IsOfNumericType()) && (type == JDType::INT)) ||
|
||||||
((cached.IsOfNumericType()) && (type == JSON_DATA_TYPE::FLOAT)))
|
((cached.IsOfNumericType()) && (type == JDType::FLOAT)))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -16,9 +16,10 @@ namespace Rest
|
|||||||
static void PostExit();
|
static void PostExit();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static bool Example_Foo(const JasonPP::JsonBlock& request, JasonPP::JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode);
|
||||||
static bool KillYourself(const JasonPP::JsonBlock& request, JasonPP::JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode);
|
static bool KillYourself(const JasonPP::JsonBlock& request, JasonPP::JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode);
|
||||||
|
|
||||||
static bool ValidateField(const std::string name, const JasonPP::JSON_DATA_TYPE type, const JasonPP::Json& checkThat, JasonPP::JsonBlock& putErrorResponseHere);
|
static bool ValidateField(const std::string name, const JasonPP::JDType type, const JasonPP::Json& checkThat, JasonPP::JsonBlock& putErrorResponseHere);
|
||||||
|
|
||||||
static Logging::Logger* log;
|
static Logging::Logger* log;
|
||||||
};
|
};
|
||||||
|
@ -86,7 +86,7 @@
|
|||||||
<ClCompile>
|
<ClCompile>
|
||||||
<WarningLevel>Level3</WarningLevel>
|
<WarningLevel>Level3</WarningLevel>
|
||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>JASONPP_RENDER_SORTED;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
@ -139,6 +139,7 @@
|
|||||||
</Link>
|
</Link>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ClCompile Include="FileSystem.cpp" />
|
||||||
<ClCompile Include="Framework.cpp" />
|
<ClCompile Include="Framework.cpp" />
|
||||||
<ClCompile Include="JasonPP.cpp" />
|
<ClCompile Include="JasonPP.cpp" />
|
||||||
<ClCompile Include="Logger.cpp" />
|
<ClCompile Include="Logger.cpp" />
|
||||||
@ -147,10 +148,12 @@
|
|||||||
<ClCompile Include="mongoose.c" />
|
<ClCompile Include="mongoose.c" />
|
||||||
<ClCompile Include="RestQueryHandler.cpp" />
|
<ClCompile Include="RestQueryHandler.cpp" />
|
||||||
<ClCompile Include="RestResponseTemplates.cpp" />
|
<ClCompile Include="RestResponseTemplates.cpp" />
|
||||||
<ClCompile Include="RestInterface.cpp" />
|
<ClCompile Include="HttpServer.cpp" />
|
||||||
|
<ClCompile Include="XGConfig.cpp" />
|
||||||
<ClCompile Include="XGControl.cpp" />
|
<ClCompile Include="XGControl.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ClInclude Include="FileSystem.h" />
|
||||||
<ClInclude Include="Framework.h" />
|
<ClInclude Include="Framework.h" />
|
||||||
<ClInclude Include="JasonPP.hpp" />
|
<ClInclude Include="JasonPP.hpp" />
|
||||||
<ClInclude Include="Logger.h" />
|
<ClInclude Include="Logger.h" />
|
||||||
@ -159,7 +162,8 @@
|
|||||||
<ClInclude Include="mongoose.h" />
|
<ClInclude Include="mongoose.h" />
|
||||||
<ClInclude Include="RestQueryHandler.h" />
|
<ClInclude Include="RestQueryHandler.h" />
|
||||||
<ClInclude Include="RestResponseTemplates.h" />
|
<ClInclude Include="RestResponseTemplates.h" />
|
||||||
<ClInclude Include="RestInterface.h" />
|
<ClInclude Include="HttpServer.h" />
|
||||||
|
<ClInclude Include="XGConfig.h" />
|
||||||
<ClInclude Include="XGControl.h" />
|
<ClInclude Include="XGControl.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
@ -36,9 +36,6 @@
|
|||||||
<ClCompile Include="Framework.cpp">
|
<ClCompile Include="Framework.cpp">
|
||||||
<Filter>Quelldateien</Filter>
|
<Filter>Quelldateien</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="RestInterface.cpp">
|
|
||||||
<Filter>Quelldateien</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="RestResponseTemplates.cpp">
|
<ClCompile Include="RestResponseTemplates.cpp">
|
||||||
<Filter>Quelldateien</Filter>
|
<Filter>Quelldateien</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
@ -48,6 +45,15 @@
|
|||||||
<ClCompile Include="XGControl.cpp">
|
<ClCompile Include="XGControl.cpp">
|
||||||
<Filter>Quelldateien</Filter>
|
<Filter>Quelldateien</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="XGConfig.cpp">
|
||||||
|
<Filter>Quelldateien</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="FileSystem.cpp">
|
||||||
|
<Filter>Quelldateien</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="HttpServer.cpp">
|
||||||
|
<Filter>Quelldateien</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="JasonPP.hpp">
|
<ClInclude Include="JasonPP.hpp">
|
||||||
@ -68,9 +74,6 @@
|
|||||||
<ClInclude Include="Framework.h">
|
<ClInclude Include="Framework.h">
|
||||||
<Filter>Headerdateien</Filter>
|
<Filter>Headerdateien</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="RestInterface.h">
|
|
||||||
<Filter>Headerdateien</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="RestResponseTemplates.h">
|
<ClInclude Include="RestResponseTemplates.h">
|
||||||
<Filter>Headerdateien</Filter>
|
<Filter>Headerdateien</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@ -80,5 +83,14 @@
|
|||||||
<ClInclude Include="XGControl.h">
|
<ClInclude Include="XGControl.h">
|
||||||
<Filter>Headerdateien</Filter>
|
<Filter>Headerdateien</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="XGConfig.h">
|
||||||
|
<Filter>Headerdateien</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="FileSystem.h">
|
||||||
|
<Filter>Headerdateien</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="HttpServer.h">
|
||||||
|
<Filter>Headerdateien</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
200
Tubio/XGConfig.cpp
Normal file
200
Tubio/XGConfig.cpp
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
#include "XGConfig.h"
|
||||||
|
|
||||||
|
using namespace JasonPP;
|
||||||
|
|
||||||
|
void XGConfig::PreInit()
|
||||||
|
{
|
||||||
|
log = new ::Logging::Logger("Config");
|
||||||
|
|
||||||
|
InitializeDefaultValues();
|
||||||
|
|
||||||
|
if (FileSystem::Exists(XGCONFIG_FILE))
|
||||||
|
{
|
||||||
|
Load();
|
||||||
|
log->cout << "Loaded config file";
|
||||||
|
log->Flush();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Save default config values, and thus, create a default config file
|
||||||
|
Save();
|
||||||
|
log->cout << log->Warn() << "Config file not found. Created default one.";
|
||||||
|
log->Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool XGConfig::IsJsonFieldValid(const JsonBlock& json, const std::string key, const JDType type)
|
||||||
|
{
|
||||||
|
return ((json.DoesShorthandExist(key)) && (json.ShorthandGet(key).GetDataType() == type));
|
||||||
|
}
|
||||||
|
|
||||||
|
void XGConfig::InitializeDefaultValues()
|
||||||
|
{
|
||||||
|
httpServer.port = "6969";
|
||||||
|
httpServer.pollingRate = 100;
|
||||||
|
httpServer.rootdir = "frontend";
|
||||||
|
|
||||||
|
logging.logfile_text = "log.txt";
|
||||||
|
logging.logfile_json = "log.json";
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void XGConfig::LoadFromJson(const JasonPP::JsonBlock& json)
|
||||||
|
{
|
||||||
|
if (IsJsonFieldValid(json, "logging.logfile_json", JDType::STRING))
|
||||||
|
{
|
||||||
|
logging.logfile_json = json.ShorthandGet("logging.logfile_json").AsString;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsJsonFieldValid(json, "logging.logfile_text", JDType::STRING))
|
||||||
|
{
|
||||||
|
logging.logfile_text = json.ShorthandGet("logging.logfile_text").AsString;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (IsJsonFieldValid(json, "httpServer.port", JDType::STRING))
|
||||||
|
{
|
||||||
|
httpServer.port = json.ShorthandGet("httpServer.port").AsString;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsJsonFieldValid(json, "httpServer.pollingRate", JDType::INT))
|
||||||
|
{
|
||||||
|
httpServer.pollingRate = json.ShorthandGet("httpServer.pollingRate").AsInt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsJsonFieldValid(json, "httpServer.rootdir", JDType::STRING))
|
||||||
|
{
|
||||||
|
httpServer.rootdir = json.ShorthandGet("httpServer.rootdir").AsString;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonBlock XGConfig::CreateJson()
|
||||||
|
{
|
||||||
|
return JsonBlock({
|
||||||
|
Ele("httpServer", JsonBlock({
|
||||||
|
Ele("port", httpServer.port),
|
||||||
|
Ele("pollingRate", httpServer.pollingRate),
|
||||||
|
Ele("rootdir", httpServer.rootdir)
|
||||||
|
})),
|
||||||
|
Ele("logging", JsonBlock({
|
||||||
|
Ele("logfile_text", logging.logfile_text),
|
||||||
|
Ele("logfile_json", logging.logfile_json)
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void XGConfig::PostExit()
|
||||||
|
{
|
||||||
|
Save();
|
||||||
|
|
||||||
|
delete log;
|
||||||
|
|
||||||
|
log = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void XGConfig::Save()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
All this shit is basically:
|
||||||
|
-- Create backup of known good config
|
||||||
|
-- Save new config
|
||||||
|
-- Is new config parsable?
|
||||||
|
-- Yes
|
||||||
|
-- Delete backup
|
||||||
|
-- No
|
||||||
|
-- Restore backup
|
||||||
|
*/
|
||||||
|
|
||||||
|
log->cout << "Saving config...";
|
||||||
|
log->Flush();
|
||||||
|
|
||||||
|
|
||||||
|
if (!FileSystem::Copy(XGCONFIG_FILE, XGCONFIG_FILE_TMPBACKUP)) // Copy known-good file
|
||||||
|
{
|
||||||
|
log->cout << log->Err() << "Unable to create backup of config file! (backup is \"" << XGCONFIG_FILE_TMPBACKUP << "\")";
|
||||||
|
log->Flush();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SaveToFile(XGCONFIG_FILE)) // Now overwrite original file
|
||||||
|
{
|
||||||
|
log->cout << log->Err() << "Unable to save config file \"" << XGCONFIG_FILE_TMPBACKUP << "\"";
|
||||||
|
log->Flush();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string writtenCode = FileSystem::ReadFile(XGCONFIG_FILE);
|
||||||
|
if (IsJsonValid(writtenCode))
|
||||||
|
{
|
||||||
|
// All good, was successfully saved. Now delete the backup file.
|
||||||
|
FileSystem::Delete(std::string(XGCONFIG_FILE) + "___");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Saving was not successful. Let's copy the backup back.
|
||||||
|
if (FileSystem::Copy(XGCONFIG_FILE_TMPBACKUP, XGCONFIG_FILE)) // Copy known-good file
|
||||||
|
{
|
||||||
|
log->cout << log->Err() << "Unable to restore backup of config file! (backup is \"" << XGCONFIG_FILE_TMPBACKUP << "\")";
|
||||||
|
log->Flush();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FileSystem::Delete(XGCONFIG_FILE_TMPBACKUP))
|
||||||
|
{
|
||||||
|
log->cout << log->Err() << "Unable to delete backup of config file! (backup is \"" << XGCONFIG_FILE_TMPBACKUP << "\")";
|
||||||
|
log->Flush();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log->cout << log->Warn() << "Unable to save config file to \"" << XGCONFIG_FILE << "\"!";
|
||||||
|
log->Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool XGConfig::SaveToFile(std::string filename)
|
||||||
|
{
|
||||||
|
Json cfgStruct(CreateJson());
|
||||||
|
return FileSystem::WriteFile(filename, cfgStruct.Render());
|
||||||
|
}
|
||||||
|
|
||||||
|
void XGConfig::Load()
|
||||||
|
{
|
||||||
|
std::string config_code = FileSystem::ReadFile(XGCONFIG_FILE);
|
||||||
|
|
||||||
|
if (IsJsonValid(config_code))
|
||||||
|
{
|
||||||
|
Json json;
|
||||||
|
json.Parse(config_code);
|
||||||
|
|
||||||
|
if (json.GetDataType() == JDType::JSON)
|
||||||
|
{
|
||||||
|
LoadFromJson(json.AsJson);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log->cout << log->Warn() << "Unable to load config file because its content is not of json-type json!";
|
||||||
|
log->Flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log->cout << log->Warn() << "Unable to parse config file to \"" << XGCONFIG_FILE << "\" because it's json syntax is most likely fucked up!";
|
||||||
|
log->Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
XGConfig::HttpServer XGConfig::httpServer;
|
||||||
|
XGConfig::Logging XGConfig::logging;
|
||||||
|
::Logging::Logger* XGConfig::log;
|
43
Tubio/XGConfig.h
Normal file
43
Tubio/XGConfig.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Filesystem.h"
|
||||||
|
#include "JasonPP.hpp"
|
||||||
|
#include "Logger.h"
|
||||||
|
|
||||||
|
#define XGCONFIG_FILE "config.json"
|
||||||
|
#define XGCONFIG_FILE_TMPBACKUP (std::string(XGCONFIG_FILE) + "___")
|
||||||
|
|
||||||
|
class XGConfig
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct HttpServer
|
||||||
|
{
|
||||||
|
std::string port;
|
||||||
|
std::string rootdir;
|
||||||
|
int pollingRate;
|
||||||
|
};
|
||||||
|
struct Logging
|
||||||
|
{
|
||||||
|
std::string logfile_text;
|
||||||
|
std::string logfile_json;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void PreInit();
|
||||||
|
static void Save();
|
||||||
|
static void PostExit();
|
||||||
|
|
||||||
|
static HttpServer httpServer;
|
||||||
|
static XGConfig::Logging logging;
|
||||||
|
|
||||||
|
static ::Logging::Logger* log;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static bool SaveToFile(std::string filename);
|
||||||
|
|
||||||
|
static bool IsJsonFieldValid(const JasonPP::JsonBlock& json, const std::string key, const JasonPP::JDType type);
|
||||||
|
static void InitializeDefaultValues();
|
||||||
|
static JasonPP::JsonBlock CreateJson();
|
||||||
|
static void LoadFromJson(const JasonPP::JsonBlock& json);
|
||||||
|
static void Load();
|
||||||
|
};
|
||||||
|
|
@ -1,17 +1,32 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Hello, World!</title>
|
<title>Hello, World!</title>
|
||||||
</head>
|
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
|
||||||
<body style="position: relative;">
|
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
|
||||||
|
<link rel="stylesheet" href="/style.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
<div style="width: 100%; height: 100vh; display: flex; justify-content: center; align-items: center; text-align: center; font-size: 38pt; font-family: sans-serif;">
|
<body>
|
||||||
<p>
|
|
||||||
HELLO, WORLD :3
|
<h1 id="headline">Hello, World!</h1>
|
||||||
</p>
|
|
||||||
</div>
|
<div class="button-wrapper">
|
||||||
|
<div class="button" id="btn_foo">
|
||||||
</body>
|
<p>
|
||||||
|
Foo!
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="button" id="btn_killserver">
|
||||||
|
<p>
|
||||||
|
Kill server
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="/script.js"></script>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
27
Tubio/frontend/script.js
Normal file
27
Tubio/frontend/script.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
$(".button").mousedown(function(e){
|
||||||
|
// Animations
|
||||||
|
e.target.classList.add("clicked");
|
||||||
|
setTimeout(function(){
|
||||||
|
e.target.classList.remove("clicked");
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#btn_foo").click(function(e){
|
||||||
|
// Functionality
|
||||||
|
axios.post("/api", {
|
||||||
|
request: "foo"
|
||||||
|
}).then(function(response){
|
||||||
|
// Do something with the servers response
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#btn_killserver").click(function(e){
|
||||||
|
// Functionality
|
||||||
|
axios.post("/api", {
|
||||||
|
request: "kill_yourself"
|
||||||
|
}).then(function(response){
|
||||||
|
if (response.data.status == "OK") {
|
||||||
|
$("#headline").html("Sever's dead");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
75
Tubio/frontend/style.css
Normal file
75
Tubio/frontend/style.css
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
body {
|
||||||
|
background-color: #00aa77;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
p {
|
||||||
|
font-family: sans-serif;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 38pt;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-wrapper {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
background-color: #ee8800;
|
||||||
|
padding: 0.5em 8em;
|
||||||
|
width: 200px;
|
||||||
|
border-radius: 1em;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: center;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
transition:
|
||||||
|
background-color 0.2s,
|
||||||
|
transform 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.button {
|
||||||
|
width: 80%;
|
||||||
|
padding: 0.15em 3em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:first-child
|
||||||
|
{
|
||||||
|
margin-top: 5em;
|
||||||
|
}
|
||||||
|
.button:not(:first-child)
|
||||||
|
{
|
||||||
|
margin-top: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 600px) {
|
||||||
|
.button:hover {
|
||||||
|
background-color: #ffaa00;
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.button.clicked {
|
||||||
|
background-color: #cc6600;
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button p{
|
||||||
|
font-size: 24pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button * {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user