Updated privacy policy, Fixed many issues, started working on other parts of the website

Update .dockerignore, .gitignore, and 27 more files...
This commit is contained in:
2022-03-18 21:03:00 +01:00
parent 2288eb64d0
commit a04c63e8bb
29 changed files with 30832 additions and 35 deletions

View File

@@ -2,5 +2,5 @@
.idea/
.dockerignore
.gitignore
*.txt
notes.txt
*.md

5
.gitignore vendored
View File

@@ -4,11 +4,10 @@
# Static resources
resources/Azias/imgs/*.exe
resources/Azias/imgs/*.url
resources/ChartJs/
resources/FontAwesomePro/
resources/GoogleFonts/
resources/HalfMoon/
# Other folders (Will be removed once the content system is finished !)
content/page/
/files/
# /files/*/
files/

View File

@@ -3,6 +3,7 @@ set_include_path('../commons/');
include_once 'config.php';
include_once 'langs.php';
?>
<!-- FIXME: Add a section about project phylosophy -> About archival -->
<!DOCTYPE html>
<html lang="<?php echo($user_language); ?>">
<head>
@@ -74,7 +75,6 @@ include_once 'langs.php';
</div>
<div class="card p-0 mx-0">
<div class="px-card py-10 border-bottom px-20">
<div class="container-fluid">
<div class="row">
@@ -89,17 +89,14 @@ include_once 'langs.php';
</div>
</div>
</div>
<div class="px-card py-10 bg-light-lm rounded-bottom px-20 bg-very-dark title-bkgd">
<h3 class="font-size-16 font-weight-semi-bold">
<i class="fad fa-file-certificate"></i>&nbsp;&nbsp;TODO
</h3>
</div>
</div>
<div class="card p-0 mx-0">
<div class="px-card py-10 border-bottom px-20">
<div class="container-fluid">
<div class="row">
@@ -114,13 +111,33 @@ include_once 'langs.php';
</div>
</div>
</div>
<div class="px-card py-10 bg-light-lm rounded-bottom px-20 bg-very-dark title-bkgd">
<h3 class="font-size-16 font-weight-semi-bold">
<i class="fad fa-file-certificate"></i>&nbsp;&nbsp;TODO
</h3>
</div>
</div>
<div class="card p-0 mx-0">
<div class="px-card py-10 border-bottom px-20">
<div class="container-fluid">
<div class="row">
<div class="col-5 col-lg-12">
<h2 class="card-title font-size-18 m-0">
<i class="fad fa-head-side-brain"></i>&nbsp;&nbsp;<?php print(localize("about.philosophy.title")); ?>
</h2>
</div>
<div class="col-7 hidden-lg-and-up text-right font-italic">
<h2 class="card-title font-size-18 m-0 text-super-muted">@NibblePoker</h2>
</div>
</div>
</div>
</div>
<div class="px-card py-10 bg-light-lm rounded-bottom px-20 bg-very-dark title-bkgd">
<h3 class="font-size-16 font-weight-semi-bold">
<i class="fad fa-file-certificate"></i>&nbsp;&nbsp;TODO
</h3>
</div>
</div>
</div>

69
commons/listing.php Normal file
View File

@@ -0,0 +1,69 @@
<?php
// Making sure the file is included.
if(basename(__FILE__) == basename($_SERVER["SCRIPT_FILENAME"])) {
header('HTTP/1.1 403 Forbidden');
die();
}
// Classes
class ListingFile {
public string $fileName;
public int $fileSize;
public string $absolutePath;
function __construct($fileName, $fileSize, $absolutePath) {
$this->fileName = $fileName;
$this->fileSize = $fileSize;
$this->absolutePath = $absolutePath;
}
}
class ListingContainer {
public string $relativePath;
public array $subDirectories;
public array $files;
function __construct($relativePath) {
$this->relativePath = $relativePath;
$this->subDirectories = array();
$this->files = array();
}
}
// Functions.
function getDirectoryContent(string $dirPath, array $filteringRegexes, int $recursiveBudget, bool $validatePermission): ListingContainer {
$returnedData = new ListingContainer("./");
if($recursiveBudget == 0) {
return $returnedData;
}
if($validatePermission) {
if(!file_exists($dirPath . DIRECTORY_SEPARATOR . ".allow_listing")) {
$returnedData->files[] = new ListingFile(
"Directory listing isn't allowed !",
-1,
realpath($dirPath . DIRECTORY_SEPARATOR . ".allow_listing")
);
return $returnedData;
}
}
foreach (scandir($dirPath) as $key => $value) {
$path = realpath($dirPath . DIRECTORY_SEPARATOR . $value);
if(is_file($path)) {
$returnedData->files[] = new ListingFile(basename($path), filesize($path), realpath($path));
} elseif($value != "." && $value != "..") {
$returnedData->subDirectories[] = getDirectoryContent(
$path,
$filteringRegexes,
$recursiveBudget - 1,
false
);
}
}
return $returnedData;
}
?>

View File

@@ -53,6 +53,11 @@ if(!isset($SIDEBAR_ID)) {
<span class="sidebar-icon"><i class="fab fa-docker"></i></span>
<?php print(localize("programming.docker.title")); ?>
</a>
<div class="sidebar-divider"></div>
<a id="sbl-projects-downloads" href="https://files.nibblepoker.lu/" class="sidebar-link sidebar-link-with-icon">
<span class="sidebar-icon"><i class="fad fa-download"></i></span>
<?php print(localize("programming.downloads.title")); ?>
</a>
</div>
<div class="sidebar-divider"></div>
<a id="sbl-links" href="<?php print(l10n_url_abs('/links/')); ?>" class="sidebar-link sidebar-link-with-icon<?php if($SIDEBAR_ID=="links"){echo(" active");} ?>">

View File

@@ -16,6 +16,7 @@
"programming.others.title": "Others",
"programming.docker.title": "Docker",
"programming.apps.title": "Applications",
"programming.downloads.title": "Downloads",
"programming.tutorials.title": "Tutorials",
"programming.tools.title": "Tools",
"electronics.title": "Electronics",
@@ -48,6 +49,42 @@
"privacy.complaint.title": "How to contact the appropriate authorities ?",
"privacy.complaint.text.1": "Should you wish to report a complaint or if you feel that our privacy policy has not addressed your concern in a satisfactory manner, you may contact your national Data Protection Authority (DPA).",
"privacy.complaint.text.2": "More information on this procedure can be found on the following websites:",
"privacy.v2.data.title": "Data collection",
"privacy.v2.data.intro.1": "This websites only collects data through generic access logs in order to detect and block bad actors from accessing this website.",
"privacy.v2.data.intro.2": "None of the data collected is used for any other purpose,it is never shared with any other third-party and is never use in any sort of analytics.",
"privacy.v2.data.private.1": "Here is the list of private data being collected:",
"privacy.v2.data.private_list.1": "IP address forwarded to us by <i>CloudFlare</i>",
"privacy.v2.data.private_list.2": "Browser's User-Agent",
"privacy.v2.data.non_private.1": "And here is the list of non-private data being collected:",
"privacy.v2.data.non_private_list.1": "Requested resource' URI",
"privacy.v2.data.non_private_list.2": "Date and time",
"privacy.v2.data.end.1": "Once the data has been logged in the access logs, it is automatically retrieved and processed by a locally-hosted application every 30 seconds and then deleted from said logs.",
"privacy.v2.data.end.2": "This application compares this information against a list of known threat sources, targets and behaviours and if a match is found, the private information is anonymized and stored for 7 days pending a manual review.",
"privacy.v2.data.end.3": "Once that 7 day period has elapsed, or once the report has been reviewed, all the relevant data is automatically deleted.",
"privacy.v2.data.end.4": "If your request wasn't flagged as potentially malicious, every data collected from it is thrown out instantly.",
"privacy.v2.data.end.5": "This process should normally never be triggered for regular traffic since most of the triggering actions are ones that should not be possible to accomplish through normal browsing.",
"privacy.v2.data.end.6": "However, this process isn't infallible and there is always an off chance that false positives may happen.",
"privacy.v2.update.intro.1": "The content of this privacy policy was originally written on the 4th of December 2021 and was last updated on the 18th of March 2022.",
"privacy.v2.update.history.1.date": "2021/12/04",
"privacy.v2.update.history.1.desc.1": "Original version",
"privacy.v2.update.history.2.date": "2022/03/18",
"privacy.v2.update.history.2.desc.1": "Changed section on data collection to reflect new policy.",
"privacy.v2.update.history.2.desc.2": "Added mention about CloudFlare and linked to their privacy policy.",
"privacy.v2.update.history.2.desc.3": "Improved the <i>\"Changes to our privacy policy\"</i> section.",
"privacy.v2.update.end.2": "In the event of a change to our privacy policy, you will be informed explicitly, and a copy of previous versions of the policy will be available through this page.",
"privacy.v2.third.title": "Third Parties",
"privacy.v2.third.intro.1": "Our websites uses some services provided by <i>CloudFlare</i> in order to prevent bad actors from accessing this website via their <i>\"Web Application Firewall\"</i>, as well as for caching purpose in order to improve your browsing experience.",
"privacy.v2.third.intro.2": "None of the data that may be gathered by <i>CloudFlare</i> is ever used.",
"privacy.v2.third.intro.3": "If you'd wish to consult their privacy policy, you can do so on their <i>\"Trust Hub\"</i> at the following URLs:",
"privacy.v2.cookies.title": "Cookies",
"privacy.v2.cookies.intro.1": "Our websites doesn't use nor store any cookies in your browser.",
"privacy.v2.cookies.intro.2": "In the unlikely event one is present, we recommend you check CloudFlare's privacy policy on their <i>\"Trust Hub\"</i> at the following URLs:",
"privacy.v2.cookies.intro.3": "This can be caused by malicious traffic coming from your network, or when you visit some private and restricted subdomains.",
"error.common.details.title": "Error details",
"error.4xx.title": "HTTP Client Error",
"error.4xx.text": "${error.4xx.text}",
@@ -75,9 +112,15 @@
"content.search.count.multiple": "results",
"about.biography.title": "Who am I ?",
"about.philosophy.title": "Projects philosophy",
"about.skills.title": "Skills",
"about.work.title": "Professional experiences",
"about.education.title": "TODO:SchoolAndEducation"
"about.education.title": "TODO:SchoolAndEducation",
"downloads.title": "Downloads",
"downloads.description": "$downloads.description",
"downloads.title.header": "Downloads",
"downloads.intro.title": "Directory listing of: <i>/files/</i>"
},
"fr": {
"home.title.nav": "Accueil",
@@ -95,6 +138,7 @@
"programming.others.title": "Autres",
"programming.docker.title": "Docker",
"programming.apps.title": "Applications",
"programming.downloads.title": "Téléchargements",
"programming.tutorials.title": "Tutoriels",
"programming.tools.title": "Outils",
"electronics.title": "Électronique",
@@ -127,6 +171,42 @@
"privacy.complaint.title": "Comment contacter les autorités compétentes ?",
"privacy.complaint.text.1": "Dans l'éventualité où vous souhaiteriez déposer une plainte pour une quelconque raison en rapport avec notre politique de confidentialité, veuillez vous adresser à l'autorité nationale de protection des données (DPA).",
"privacy.complaint.text.2": "Les informations concernant cette procédure peuvent être trouvées sur les sites internet suivants:",
"privacy.v2.data.title": "Collecte de données",
"privacy.v2.data.intro.1": "Ce site web collecte des données au travers des journaux d'évènements afin de détecter et bloquer de potentiels acteurs malveillants.",
"privacy.v2.data.intro.2": "Les données collectées ne sont en aucun cas utilisées pour une quelconque autre raison.",
"privacy.v2.data.private.1": "Voici la liste des données personelles collectées:",
"privacy.v2.data.private_list.1": "L'addresse IP source détéctée par <i>CloudFlare</i>",
"privacy.v2.data.private_list.2": "Le \"User-Agent\" de votre navigateur internet",
"privacy.v2.data.non_private.1": "Et voici la liste des données non-personelles collectées:",
"privacy.v2.data.non_private_list.1": "L'URI de la ressource demandée",
"privacy.v2.data.non_private_list.2": "La date et l'heure",
"privacy.v2.data.end.1": "Les données présentes dans les journaux d'évènements sont extraites et retirées pour être traitées par une application qui tourne en local toute les 30 secondes.",
"privacy.v2.data.end.2": "L'application en question utilise et compare ces informations avec une liste d'acteurs malveillants et comportement suspects connus.<br>Si une correspondance est détectée, les informations privées sont anonymisées et sont stockées pendant 7 jours en attendant qu'un examen manuel soit effectué.",
"privacy.v2.data.end.3": "Après cette période de 7 jours, ou après qu'un examen ait été effectué, les données sont automatiquement supprimées.",
"privacy.v2.data.end.4": "Toute information concernant une requête non suspecte est automatiquement supprimée.",
"privacy.v2.data.end.5": "Ce processus n'est normalement jamais utilisé sur des requêtes provenant d'un trafic légitime dû au fait qu'il isole principalement les actions et requêtes qui ne devraient pas être possible sur ce site.",
"privacy.v2.data.end.6": "Cependant, ce processus n'est pas infaillible et il est toujours possible qu'une requête soit isolée par erreur.",
"privacy.v2.update.intro.1": "Le contenu de notre politique de confidentialité a été originalement écrit et le 4 décembre 2021 modifié pour la dernière fois le 18 mars 2022.",
"privacy.v2.update.history.1.date": "2021/12/04",
"privacy.v2.update.history.1.desc.1": "Version originale",
"privacy.v2.update.history.2.date": "2022/03/18",
"privacy.v2.update.history.2.desc.1": "Mise-à-jour de la section sur la collection des données.",
"privacy.v2.update.history.2.desc.2": "Ajout d'une mention de CloudFlare et ajout de liens vers leur politique de confidentialité.",
"privacy.v2.update.history.2.desc.3": "Amélioration de la section <i>\"Changements à notre politique de confidentialité\"</i>.",
"privacy.v2.update.end.2": "En cas de changement, vous serez clairement informé et une copie des anciennes versions de notre politique sera disponible au travers de cette page.",
"privacy.v2.third.title": "Organismes tiers",
"privacy.v2.third.intro.1": "Ce site web utilise les services proposés par <i>CloudFlare</i> afin d'empêcher des acteurs malveillants d'y accéder grâce au <i>\"web application Firewall\"</i>, ainsi que pour améliorer l'expérience des personnes le visitant grâce à leur CDN.",
"privacy.v2.third.intro.2": "Aucune des données collectées et stockées par <i>CloudFlare</i> n'est utilisé.",
"privacy.v2.third.intro.3": "Si vous souhaitez consulter leur politique de confidentialité, vous pouvez le faire sur leur <i>\"Trust Hub\"</i> en utilisant les liens ci-dessous:",
"privacy.v2.cookies.title": "Cookies de navigation",
"privacy.v2.cookies.intro.1": "Ce site web n'utilise pas, et ne stocke aucun cookies dans votre navigateur internet.",
"privacy.v2.cookies.intro.2": "Dans l'éventualité où l'un serait présent, nous vous recommandons de consulter le <i>\"Trust Hub\"</i> de <i>CloudFlare</i> via les liens suivants pour plus d'informations:",
"privacy.v2.cookies.intro.3": "Ce genre de situation peut être causée par le fait que du trafic suspect provient de votre navigateur internet, ou lors d'une visite sur un de nos sous-domaines privés.",
"error.common.details.title": "Détails de l'erreur",
"error.4xx.title": "Erreur du client HTTP",
"error.4xx.text": "${error.4xx.text}",
@@ -154,6 +234,7 @@
"content.search.count.multiple": "résultats",
"about.biography.title": "Qui suis-je ?",
"about.philosophy.title": "Philosophie des projets",
"about.skills.title": "Compétences",
"about.work.title": "Parcours professionnel",
"about.education.title": "Éducation"

View File

@@ -145,8 +145,13 @@ include_once 'langs.php';
<div class="px-card py-5 px-20">
<p>
1st February 2022<br>
Going the self-hosted route.
<span class="font-weight-bold">18th March 2022</span><br>
Updated the privacy policy.
</p>
<hr>
<p>
<span class="font-weight-bold">28th February 2022</span><br>
Dropped OVH as our main web hosting provider, and switched to CloudFlare.
</p>
</div>

View File

@@ -58,16 +58,113 @@ include_once 'langs.php';
<div class="px-card py-10 border-bottom px-20">
<div class="container-fluid">
<h2 class="card-title font-size-18 m-0">
<i class="fad fa-database"></i>&nbsp;&nbsp;<?php print(localize('privacy.data.title')); ?>
<i class="fad fa-database"></i>&nbsp;&nbsp;<?php print(localize('privacy.v2.data.title')); ?>
</h2>
</div>
</div>
<div class="px-card py-10 bg-light-lm px-20 bg-very-dark title-bkgd">
<p class="font-size-12 m-0">
<?php print(localize('privacy.data.text.1')); ?>
<?php print(localize('privacy.v2.data.intro.1')); ?>
<br>
<?php print(localize('privacy.v2.data.intro.2')); ?>
</p>
<p class="font-size-12 mb-0">
<?php print(localize('privacy.v2.data.private.1')); ?><br>
<span class="m-0 ml-10">
<i class="fad fa-circle font-size-8"></i>&nbsp;&nbsp;<?php print(localize('privacy.v2.data.private_list.1')); ?><br>
</span>
<span class="m-0 ml-10">
<i class="fad fa-circle font-size-8"></i>&nbsp;&nbsp;<?php print(localize('privacy.v2.data.private_list.2')); ?><br>
</span>
</p>
<p class="font-size-12 mb-0">
<?php print(localize('privacy.v2.data.non_private.1')); ?><br>
<span class="m-0 ml-10">
<i class="fad fa-circle font-size-8"></i>&nbsp;&nbsp;<?php print(localize('privacy.v2.data.non_private_list.1')); ?><br>
</span>
<span class="m-0 ml-10">
<i class="fad fa-circle font-size-8"></i>&nbsp;&nbsp;<?php print(localize('privacy.v2.data.non_private_list.2')); ?><br>
</span>
</p>
<p class="font-size-12 mb-0">
<?php print(localize('privacy.v2.data.end.1')); ?>
</p>
<p class="font-size-12 mb-0">
<?php print(localize('privacy.v2.data.end.2')); ?><br>
<?php print(localize('privacy.v2.data.end.3')); ?>
</p>
<p class="font-size-12 mb-0">
<?php print(localize('privacy.v2.data.end.4')); ?>
</p>
<p class="font-size-12 mb-0">
<?php print(localize('privacy.v2.data.end.5')); ?><br>
<?php print(localize('privacy.v2.data.end.6')); ?>
</p>
</div>
</div>
<div class="card p-0 mx-0">
<div class="px-card py-10 border-bottom px-20">
<div class="container-fluid">
<h2 class="card-title font-size-18 m-0">
<i class="fad fa-handshake"></i>&nbsp;&nbsp;<?php print(localize('privacy.v2.third.title')); ?>
</h2>
</div>
</div>
<div class="px-card py-10 bg-light-lm px-20 bg-very-dark title-bkgd">
<p class="font-size-12 m-0">
<?php print(localize('privacy.v2.third.intro.1')); ?>
</p>
<p class="font-size-12 mb-0">
<?php print(localize('privacy.v2.third.intro.2')); ?>
</p>
<p class="font-size-12 mb-0">
<?php print(localize('privacy.v2.third.intro.3')); ?><br>
<i class="fad fa-globe ml-10"></i>
<a href="https://www.cloudflare.com/trust-hub/gdpr/">
https://www.cloudflare.com/trust-hub/gdpr/
</a>
<span class="ml-5">(<?php print(localize('lang.english')); ?>)</span><br>
<i class="fad fa-globe ml-10"></i>
<a href="https://www.cloudflare.com/fr-fr/trust-hub/gdpr/">
https://www.cloudflare.com/fr-fr/trust-hub/gdpr/
</a>
<span class="ml-5">(<?php print(localize('lang.french')); ?>)</span><br>
</p>
</div>
</div>
<div class="card p-0 mx-0">
<div class="px-card py-10 border-bottom px-20">
<div class="container-fluid">
<h2 class="card-title font-size-18 m-0">
<i class="fad fa-cookie-bite"></i>&nbsp;&nbsp;<?php print(localize('privacy.v2.cookies.title')); ?>
</h2>
</div>
</div>
<div class="px-card py-10 bg-light-lm px-20 bg-very-dark title-bkgd">
<p class="font-size-12 m-0">
<?php print(localize('privacy.v2.cookies.intro.1')); ?>
</p>
<p class="font-size-12 mb-0">
<?php print(localize('privacy.v2.cookies.intro.2')); ?><br>
<i class="fad fa-globe ml-10"></i>
<a href="https://www.cloudflare.com/trust-hub/gdpr/">
https://www.cloudflare.com/trust-hub/gdpr/
</a>
<span class="ml-5">(<?php print(localize('lang.english')); ?>)</span><br>
<i class="fad fa-globe ml-10"></i>
<a href="https://www.cloudflare.com/fr-fr/trust-hub/gdpr/">
https://www.cloudflare.com/fr-fr/trust-hub/gdpr/
</a>
<span class="ml-5">(<?php print(localize('lang.french')); ?>)</span><br>
</p>
<p class="font-size-12 mb-0">
<?php print(localize('privacy.v2.cookies.intro.3')); ?>
</p>
</div>
</div>
<div class="card p-0 mx-0">
<div class="px-card py-10 border-bottom px-20">
<div class="container-fluid">
@@ -77,14 +174,47 @@ include_once 'langs.php';
</div>
</div>
<div class="px-card py-10 bg-light-lm px-20 bg-very-dark title-bkgd">
<p class="font-size-12 m-0 mb-5">
<?php print(localize('privacy.update.text.1')); ?>
</p>
<p class="font-size-12 m-0">
<?php print(localize('privacy.update.text.2')); ?>
<?php print(localize('privacy.v2.update.intro.1')); ?>
</p>
<p class="font-size-12 mb-0">
<i class="fad fa-calendar-alt"></i>&nbsp;&nbsp;<?php print(localize('privacy.v2.update.history.1.date')); ?>
<span class="m-0 ml-20">
<i class="fad fa-globe"></i>&nbsp;&nbsp;<a href="/privacy/privacy_2021-12-04_en.txt"><?php print(localize('lang.english')); ?></a>
</span>
<span class="m-0 ml-20">
<i class="fad fa-globe"></i>&nbsp;&nbsp;<a href="/privacy/privacy_2021-12-04_fr.txt"><?php print(localize('lang.french')); ?></a>
</span>
<br>
<span class="m-0 ml-10">
<i class="fad fa-circle font-size-8"></i>&nbsp;&nbsp;<?php print(localize('privacy.v2.update.history.1.desc.1')); ?><br>
</span>
</p>
<p class="font-size-12 mb-0">
<i class="fad fa-calendar-alt"></i>&nbsp;&nbsp;<?php print(localize('privacy.v2.update.history.2.date')); ?>
<span class="m-0 ml-20">
<i class="fad fa-globe"></i>&nbsp;&nbsp;<a href="/privacy/privacy_2022-03-18_en.txt"><?php print(localize('lang.english')); ?></a>
</span>
<span class="m-0 ml-20">
<i class="fad fa-globe"></i>&nbsp;&nbsp;<a href="/privacy/privacy_2022-03-18_fr.txt"><?php print(localize('lang.french')); ?></a>
</span>
<br>
<span class="m-0 ml-10">
<i class="fad fa-circle font-size-8"></i>&nbsp;&nbsp;<?php print(localize('privacy.v2.update.history.2.desc.1')); ?><br>
</span>
<span class="m-0 ml-10">
<i class="fad fa-circle font-size-8"></i>&nbsp;&nbsp;<?php print(localize('privacy.v2.update.history.2.desc.2')); ?><br>
</span>
<span class="m-0 ml-10">
<i class="fad fa-circle font-size-8"></i>&nbsp;&nbsp;<?php print(localize('privacy.v2.update.history.2.desc.3')); ?>
</span>
</p>
<p class="font-size-12 mb-0">
<?php print(localize('privacy.v2.update.end.2')); ?>
</p>
</div>
</div>
<div class="card p-0 mx-0">
<div class="px-card py-10 border-bottom px-20">
<div class="container-fluid">

View File

@@ -0,0 +1,29 @@
# Introduction
This privacy policy is written in accordance with the 12th and 13th articles of the GDPR.
If you wish to consult it, you can do so on the following websites:
* https://gdpr.eu/
* https://eur-lex.europa.eu/
# What data do we collect ?
This website, does not collect any personal data, be it through access logs, cookies or any third-party services.
# Changes to our privacy policy
The content of this privacy policy was written and last updated on the 4th of December 2021.
In the event of a change to our privacy policy, you will be informed explicitly, and a copy of previous versions of the policy will be available on this page.
# How to contact us ?
If you wish to contact us for more information regarding our privacy policy, please contact us via the form included on the contact page, or at the following email address:
* herwin.bozet@gmail.com
# How to contact the appropriate authorities ?
Should you wish to report a complaint or if you feel that our privacy policy has not addressed your concern in a satisfactory manner, you may contact your national Data Protection Authority (DPA).
More information on this procedure can be found on the following websites:
* https://ec.europa.eu/ (English)
* https://gegevensbeschermingsautoriteit.be/ (French)

View File

@@ -0,0 +1,29 @@
# Introduction
La politique de confidentialité ci-présente a été écrite en accord avec les articles 12 et 13 de la RGPD.
Si vous souhaitez consulter le texte officiel en question, vous pouvez le faire sur les sites internet suivants :
* https://gdpr.eu/
* https://eur-lex.europa.eu/
# Quelles données sont collectées ?
Ce site web ne collecte aucune donnée personnelle, que ce soit au travers des journaux d'évènements sur nos serveurs, des cookies ou via quelconque autre tiers parti.
# Changements à notre politique de confidentialité
Le contenu de notre politique de confidentialité a été écrit et modifié pour la dernière fois le 4 décembre 2021.
En cas de changement, vous serez clairement informé et une copie des anciennes versions de notre politique sera disponible sur cette page.
# Comment nous contacter ?
Si vous souhaitez nous contacter afin d'obtenir plus d'informations concernant notre politique de confidentialité, nous vous recommandons d'utiliser le formulaire présent sur la page de contact, ou par courriel à l'adresse suivante:
* herwin.bozet@gmail.com
# Comment contacter les autorités compétentes ?
Dans l'éventualité où vous souhaiteriez déposer une plainte pour une quelconque raison en rapport avec notre politique de confidentialité, veuillez vous adresser à l'autorité nationale de protection des données (DPA).
Les informations concernant cette procédure peuvent être trouvées sur les sites internet suivants:
* https://ec.europa.eu/ (Anglais)
* https://gegevensbeschermingsautoriteit.be/ (Français)

View File

@@ -0,0 +1,76 @@
# Introduction
This privacy policy is written in accordance with the 12th and 13th articles of the GDPR.
If you wish to consult it, you can do so on the following websites:
https://gdpr.eu/
https://eur-lex.europa.eu/
# Data collection
This websites only collects data through generic access logs in order to detect and block bad actors from accessing this website.
None of the data collected is used for any other purpose,it is never shared with any other third-party and is never use in any sort of analytics.
Here is the list of private data being collected:
* IP address forwarded to us by CloudFlare
* Browser's User-Agent
And here is the list of non-private data being collected:
* Requested resource' URI
* Date and time
Once the data has been logged in the access logs, it is automatically retrieved and processed by a locally-hosted application every 30 seconds and then deleted from said logs.
This application compares this information against a list of known threat sources, targets and behaviours and if a match is found, the private information is anonymized and stored for 7 days pending a manual review.
Once that 7 day period has elapsed, or once the report has been reviewed, all the relevant data is automatically deleted.
If your request wasn't flagged as potentially malicious, every data collected from it is thrown out instantly.
This process should normally never be triggered for regular traffic since most of the triggering actions are ones that should not be possible to accomplish through normal browsing.
However, this process isn't infallible and there is always an off chance that false positives may happen.
# Third Parties
Our websites uses some services provided by CloudFlare in order to prevent bad actors from accessing this website via their "Web Application Firewall", as well as for caching purpose in order to improve your browsing experience.
None of the data that may be gathered by CloudFlare is ever used.
If you'd wish to consult their privacy policy, you can do so on their "Trust Hub" at the following URLs:
https://www.cloudflare.com/trust-hub/gdpr/ (English)
https://www.cloudflare.com/fr-fr/trust-hub/gdpr/ (French)
# Cookies
Our websites doesn't use nor store any cookies in your browser.
In the unlikely event one is present, we recommend you check CloudFlare's privacy policy on their "Trust Hub" at the following URLs:
https://www.cloudflare.com/trust-hub/gdpr/ (English)
https://www.cloudflare.com/fr-fr/trust-hub/gdpr/ (French)
This can be caused by malicious traffic coming from your network, or when you visit some private and restricted subdomains.
# Changes to our privacy policy
The content of this privacy policy was originally written on the 4th of December 2021 and was last updated on the 18th of March 2022.
* 2021/12/04
* Original version
* 2022/03/18
* Changed section on data collection to reflect new policy.
* Added mention about CloudFlare and linked to their privacy policy.
* Improved the "Changes to our privacy policy" section.
In the event of a change to our privacy policy, you will be informed explicitly, and a copy of previous versions of the policy will be available through this page.
# How to contact us ?
If you wish to contact us for more information regarding our privacy policy, please contact us via the form included on the contact page, or at the following email address:
* herwin.bozet@gmail.com
# How to contact the appropriate authorities ?
Should you wish to report a complaint or if you feel that our privacy policy has not addressed your concern in a satisfactory manner, you may contact your national Data Protection Authority (DPA).
More information on this procedure can be found on the following websites:
* https://ec.europa.eu/ (English)
* https://gegevensbeschermingsautoriteit.be/ (French)

View File

@@ -0,0 +1,77 @@
# Introduction
La politique de confidentialité ci-présente a été écrite en accord avec les articles 12 et 13 de la RGPD.
Si vous souhaitez consulter le texte officiel en question, vous pouvez le faire sur les sites internet suivants :
* https://gdpr.eu/
* https://eur-lex.europa.eu/
# Collecte de données
Ce site web collecte des données au travers des journaux d'évènements afin de détecter et bloquer de potentiels acteurs malveillants.
Les données collectées ne sont en aucun cas utilisées pour une quelconque autre raison.
Voici la liste des données personelles collectées:
* L'addresse IP source détéctée par CloudFlare
* Le "User-Agent" de votre navigateur internet
Et voici la liste des données non-personelles collectées:
* L'URI de la ressource demandée
* La date et l'heure
Les données présentes dans les journaux d'évènements sont extraites et retirées pour être traitées par une application qui tourne en local toute les 30 secondes.
L'application en question utilise et compare ces informations avec une liste d'acteurs malveillants et comportement suspects connus.
Si une correspondance est détectée, les informations privées sont anonymisées et sont stockées pendant 7 jours en attendant qu'un examen manuel soit effectué.
Après cette période de 7 jours, ou après qu'un examen ait été effectué, les données sont automatiquement supprimées.
Toute information concernant une requête non suspecte est automatiquement supprimée.
Ce processus n'est normalement jamais utilisé sur des requêtes provenant d'un trafic légitime dû au fait qu'il isole principalement les actions et requêtes qui ne devraient pas être possible sur ce site.
Cependant, ce processus n'est pas infaillible et il est toujours possible qu'une requête soit isolée par erreur.
# Organismes tiers
Ce site web utilise les services proposés par CloudFlare afin d'empêcher des acteurs malveillants d'y accéder grâce au "web application Firewall", ainsi que pour améliorer l'expérience des personnes le visitant grâce à leur CDN.
Aucune des données collectées et stockées par CloudFlare n'est utilisé.
Si vous souhaitez consulter leur politique de confidentialité, vous pouvez le faire sur leur "Trust Hub" en utilisant les liens ci-dessous:
* https://www.cloudflare.com/trust-hub/gdpr/ (Anglais)
* https://www.cloudflare.com/fr-fr/trust-hub/gdpr/ (Français)
# Cookies de navigation
Ce site web n'utilise pas, et ne stocke aucun cookies dans votre navigateur internet.
Dans l'éventualité où l'un serait présent, nous vous recommandons de consulter le "Trust Hub" de CloudFlare via les liens suivants pour plus d'informations:
* https://www.cloudflare.com/trust-hub/gdpr/ (Anglais)
* https://www.cloudflare.com/fr-fr/trust-hub/gdpr/ (Français)
Ce genre de situation peut être causée par le fait que du trafic suspect provient de votre navigateur internet, ou lors d'une visite sur un de nos sous-domaines privés.
# Changements à notre politique de confidentialité
Le contenu de notre politique de confidentialité a été originalement écrit et le 4 décembre 2021 modifié pour la dernière fois le 18 mars 2022.
* 2021/12/04
* Version originale
* 2022/03/18
* Mise-à-jour de la section sur la collection des données.
* Ajout d'une mention de CloudFlare et ajout de liens vers leur politique de confidentialité.
* Amélioration de la section "Changements à notre politique de confidentialité".
En cas de changement, vous serez clairement informé et une copie des anciennes versions de notre politique sera disponible au travers de cette page.
# Comment nous contacter ?
Si vous souhaitez nous contacter afin d'obtenir plus d'informations concernant notre politique de confidentialité, nous vous recommandons d'utiliser le formulaire présent sur la page de contact, ou par courriel à l'adresse suivante:
* herwin.bozet@gmail.com
# Comment contacter les autorités compétentes ?
Dans l'éventualité où vous souhaiteriez déposer une plainte pour une quelconque raison en rapport avec notre politique de confidentialité, veuillez vous adresser à l'autorité nationale de protection des données (DPA).
Les informations concernant cette procédure peuvent être trouvées sur les sites internet suivants:
* https://ec.europa.eu/ (Anglais)
* https://gegevensbeschermingsautoriteit.be/ (Français)

View File

@@ -28,21 +28,8 @@ These pages can be removed by deleting the folders and removing the appropriate
the [.htaccess](.htaccess) file.
## Requirements
These files are not present in this repo since they
would clutter the commits and potentially break some licensing rules.
### Required
These files are required for the website to work properly !<br>
These files are required and need to be installed manually for the website to work properly !<br>
* Apache & * PHP 8 or newer
* Font Awesome Pro v5.15.3
* `/resources/FontAwesomePro/`
* HalfMoon v1.1.1
* `/resources/HalfMoon/`
### Optional
These files are leftovers from previous projects and might be required somewhere.
* ChartJS
* `/resources/ChartJs/`
* Material Icons
* `/resources/GoogleFonts/MaterialIcons/`

View File

@@ -43,6 +43,12 @@ div#body-overlay {
.ucase {
text-transform: uppercase;
}
.font-size-8 {
font-size: 0.8rem!important;
}
.font-size-10 {
font-size: 1.0rem!important;
}
.font-size-26 {
font-size: 2.6rem!important;
}
@@ -142,6 +148,15 @@ div.content-tag-container a:not(:first-child) {
}
}
/* Fixes */
/* TODO: Fix it when it is closed */
div.last-inner-collapse-border-fix {
border: var(--collapse-content-border-width) solid var(--dm-collapse-content-border-color) !important;
border-top: 0 !important;
border-bottom-left-radius: var(--collapse-content-border-radius) !important;
border-bottom-right-radius: var(--collapse-content-border-radius) !important;
}
/* Trash */
/*.lang-icon {

View File

@@ -0,0 +1,32 @@
# Contributing to Halfmoon
Please read this short document if you want to contribute to Halfmoon.
## Our goal
Before getting to the issues and pull requests, it is important to understand what the framework is trying to do.
Halfmoon's stated goal is really simple - create a customizable frontend framework, with a built-in dark mode, that is great for building dashboards and tools. However, our other goals are more implicit:
- Everything has to be **super functional and snappy**. Ideally, that means no performance issues and just consistently good execution.
- Everything has to **look good** as well, and have a **single, consistent style**.
The most important thing here is building a **really good (looking) framework that also works very well**, and that should always be the top priority.
## Issues
Please keep the following things in mind when raising issues:
- Feature requests are welcome. However, please make sure that the feature you are requesting falls under the scope of the project.
- **Do not** post personal support questions as issues. For those, use this [tag on Stack Overflow](https://stackoverflow.com/questions/tagged/halfmoon).
- When raising an issue, please be as thorough as possible. For possible bugs, reproducible code is always a great sign.
## Pull requests
**Please ask first** before you put in work for a pull request. Things are constantly being worked on, and it is very difficult to share everything publicly in a meaningful way. So asking lets us avoid working on the same thing, or worse, someone putting in a lot of work for a PR that does not fit into the scope of the project. Please do not take this the wrong way; at this stage, the project needs strong leadership to become something special.
The best way to ask is through email: [hey.halfmoon@gmail.com](mailto:hey.halfmoon@gmail.com). I am always open to proper discussions about the framework, especially when it comes to contributions and pull requests.
## License
Any contribution you make will fall under the license being used by the framework: [MIT license](https://www.gethalfmoon.com/license/).

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Halfmoon UI
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
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
SOFTWARE.

View File

@@ -0,0 +1,95 @@
# [Halfmoon](https://www.gethalfmoon.com)
> Front-end framework with a built-in dark mode and full customizability using CSS variables; great for building dashboards and tools.
- **Built-in dark mode**—Halfmoon comes with a built-in, toggleable dark mode, which is one of its most important and defining features.
- **Fully customizable using CSS variables**—The framework is built entirely using CSS variables (also known as CSS custom properties). There are close to *1,500 CSS variables*, which means that almost everything can be customized by overriding a property, making it very easy to theme Halfmoon to fit your brand. [Learn more about customization](https://www.gethalfmoon.com/docs/customize/).
- **Great for building dashboards and tools**—The components have a very standard look and feel to them, making them suitable for dashboards and tools. Moreover, a lot of importance is placed on components such as forms, navbars, sidebars, dropdowns, toasts, shortcuts, etc. and there are also *tons of utilities* available.
- **Optional JS library**—Many of the components found in Halfmoon are built to work without JavaScript. However, the framework still comes with a powerful JavaScript library with no extra dependencies, such as jQuery.
- **Bootstrap like classes**—The class names should be instantly familiar to anyone who has used Bootstrap.
- **Cross-browser compatibility**—Fully supports almost all the browsers under the sun, including really old ones like Internet Explorer 11.
To learn more, go to [the documentation](https://www.gethalfmoon.com/docs/introduction/).
## Quickstart
The quickest way to get started with Halfmoon is by using the CDN to include the following files:
```html
<!-- Halfmoon CSS -->
<link href="https://cdn.jsdelivr.net/npm/halfmoon@1.1.1/css/halfmoon-variables.min.css" rel="stylesheet" />
<!--
Or,
Use the following (no variables, supports IE11):
<link href="https://cdn.jsdelivr.net/npm/halfmoon@1.1.1/css/halfmoon.min.css" rel="stylesheet" />
-->
<!-- Halfmoon JS -->
<script src="https://cdn.jsdelivr.net/npm/halfmoon@1.1.1/js/halfmoon.min.js"></script>
```
**Pleast note**, the JS file should be placed at the end of the `<body>` tag. Otherwise, some things may not work as expected. For example, using the `onclick="..."` event to call one of Halfmoon's built-in methods will not work **unless** the JS file is placed at the end of the `<body>` tag.
## Using npm
```
npm install halfmoon
```
After installation, the required CSS and JS file can be imported in the following way:
```javascript
// Include CSS file
require("halfmoon/css/halfmoon-variables.min.css");
/*
Or,
Include the following (no variables, supports IE11):
require("halfmoon/css/halfmoon.min.css");
*/
// Import JS library
const halfmoon = require("halfmoon");
```
Please note that manual initialization is required for some components, that is, after the DOM is loaded, the following method needs to be called:
```javascript
// Call this method after the DOM has been loaded
halfmoon.onDOMContentLoaded();
```
This initializes all of the components that require JavaScript, such as dropdowns, custom file inputs, shortcuts, etc.
In this way, Halfmoon can be used with frameworks that use the virtual DOM, such as React and Vue. For instance, in the case of Vue, the `halfmoon.onDOMContentLoaded()` method would be called inside the `mounted()` hook of your component.
## Using React
If you are using React to call the built-in methods, such as `halfmoon.toggleSidebar()`, please make sure the call is made in a way that binds the correct context. There are two ways to do this:
1. Using an anonymous method:
```html
<button className="btn" type="button" onClick={() => halfmoon.toggleSidebar()}>
```
2. Using `bind`:
```html
<button className="btn" type="button" onClick={halfmoon.toggleSidebar.bind(halfmoon)}>
```
You can find more details in the [React documentation](https://reactjs.org/docs/faq-functions.html#why-is-binding-necessary-at-all).
## Starter template generator
You can use the [starter template generator](https://www.gethalfmoon.com/docs/page-building/#starter-template-generator) to generate boilerplates for your project. The generator takes your settings and adds the appropriate classes and defines the required containers and elements.
Once again, we recommend reading [the documentation](https://www.gethalfmoon.com/docs/introduction/), as it contains a lot of examples to help you quickly build websites.
## License
Halfmoon is licensed under the [MIT](https://www.gethalfmoon.com/license/) license.
## Copyright
Copyright 2020, Halfmoon UI

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

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,3 @@
var halfmoon = require("./js/halfmoon-module");
module.exports = halfmoon;

View File

@@ -0,0 +1,513 @@
/*
* -----------------------------------------------------------------------------
* Halfmoon JS (module)
* Version: 1.1.1
* https://www.gethalfmoon.com
* Copyright, Halfmoon UI
* Licensed under MIT (https://www.gethalfmoon.com/license)
* -----------------------------------------------------------------------------
* The above notice must be included in its entirety when this file is used.
*/
/* Start polyfills */
// Polyfill for Element.matches()
if (!Element.prototype.matches) {
Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;
}
// Polyfill for Element.closest()
if (!Element.prototype.closest) {
Element.prototype.closest = function(s) {
var el = this;
do {
if (el.matches(s)) return el;
el = el.parentElement || el.parentNode;
} while (el !== null && el.nodeType === 1);
return null;
};
}
// Polyfill for Element.classList (http://purl.eligrey.com/github/classList.js/blob/master/classList.js)
"document"in self&&("classList"in document.createElement("_")&&(!document.createElementNS||"classList"in document.createElementNS("http://www.w3.org/2000/svg","g"))||!function(t){"use strict";if("Element"in t){var e="classList",n="prototype",i=t.Element[n],s=Object,r=String[n].trim||function(){return this.replace(/^\s+|\s+$/g,"")},o=Array[n].indexOf||function(t){for(var e=0,n=this.length;n>e;e++)if(e in this&&this[e]===t)return e;return-1},c=function(t,e){this.name=t,this.code=DOMException[t],this.message=e},a=function(t,e){if(""===e)throw new c("SYNTAX_ERR","The token must not be empty.");if(/\s/.test(e))throw new c("INVALID_CHARACTER_ERR","The token must not contain space characters.");return o.call(t,e)},l=function(t){for(var e=r.call(t.getAttribute("class")||""),n=e?e.split(/\s+/):[],i=0,s=n.length;s>i;i++)this.push(n[i]);this._updateClassName=function(){t.setAttribute("class",this.toString())}},u=l[n]=[],h=function(){return new l(this)};if(c[n]=Error[n],u.item=function(t){return this[t]||null},u.contains=function(t){return~a(this,t+"")},u.add=function(){var t,e=arguments,n=0,i=e.length,s=!1;do t=e[n]+"",~a(this,t)||(this.push(t),s=!0);while(++n<i);s&&this._updateClassName()},u.remove=function(){var t,e,n=arguments,i=0,s=n.length,r=!1;do for(t=n[i]+"",e=a(this,t);~e;)this.splice(e,1),r=!0,e=a(this,t);while(++i<s);r&&this._updateClassName()},u.toggle=function(t,e){var n=this.contains(t),i=n?e!==!0&&"remove":e!==!1&&"add";return i&&this[i](t),e===!0||e===!1?e:!n},u.replace=function(t,e){var n=a(t+"");~n&&(this.splice(n,1,e),this._updateClassName())},u.toString=function(){return this.join(" ")},s.defineProperty){var f={get:h,enumerable:!0,configurable:!0};try{s.defineProperty(i,e,f)}catch(p){void 0!==p.number&&-2146823252!==p.number||(f.enumerable=!1,s.defineProperty(i,e,f))}}else s[n].__defineGetter__&&i.__defineGetter__(e,h)}}(self),function(){"use strict";var t=document.createElement("_");if(t.classList.add("c1","c2"),!t.classList.contains("c2")){var e=function(t){var e=DOMTokenList.prototype[t];DOMTokenList.prototype[t]=function(t){var n,i=arguments.length;for(n=0;i>n;n++)t=arguments[n],e.call(this,t)}};e("add"),e("remove")}if(t.classList.toggle("c3",!1),t.classList.contains("c3")){var n=DOMTokenList.prototype.toggle;DOMTokenList.prototype.toggle=function(t,e){return 1 in arguments&&!this.contains(t)==!e?e:n.call(this,t)}}"replace"in document.createElement("_").classList||(DOMTokenList.prototype.replace=function(t,e){var n=this.toString().split(" "),i=n.indexOf(t+"");~i&&(n=n.slice(i),this.remove.apply(this,n),this.add(e),this.add.apply(this,n.slice(1)))}),t=null}());
/* End polyfills */
/* Halfmoon JS core */
var halfmoon = {
// Getting the required elements
// Re-initialized once the DOM is loaded (to avoid issues with virtual DOM)
pageWrapper: document.getElementsByClassName("page-wrapper")[0],
stickyAlerts: document.getElementsByClassName("sticky-alerts")[0],
darkModeOn: false, // Also re-initialized once the DOM is loaded (see below)
// Create cookie
createCookie: function(name, value, days) {
var expires;
if (days) {
var date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
expires = "; expires=" + date.toGMTString();
}
else {
expires = "";
}
document.cookie = name + "=" + value + expires + "; path=/";
},
// Read cookie
readCookie: function(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(";");
for(var i=0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) === " ") {
c = c.substring(1, c.length);
}
if (c.indexOf(nameEQ) === 0) {
return c.substring(nameEQ.length,c.length);
}
}
return null;
},
// Erase cookie
eraseCookie: function(name) {
this.createCookie(name, "", -1);
},
// Toggle light/dark mode
toggleDarkMode: function() {
if (document.body.classList.contains("dark-mode")) {
document.body.classList.remove("dark-mode");
this.darkModeOn = false;
this.createCookie("halfmoon_preferredMode", "light-mode", 365);
} else {
document.body.classList.add("dark-mode");
this.darkModeOn = true;
this.createCookie("halfmoon_preferredMode", "dark-mode", 365);
}
},
// Get preferred mode
getPreferredMode: function() {
if (this.readCookie("halfmoon_preferredMode")) {
return this.readCookie("halfmoon_preferredMode");
} else {
return "not-set";
}
},
// Toggles sidebar
toggleSidebar: function() {
if (this.pageWrapper) {
if (this.pageWrapper.getAttribute("data-sidebar-hidden")) {
this.pageWrapper.removeAttribute("data-sidebar-hidden");
} else {
this.pageWrapper.setAttribute("data-sidebar-hidden", "hidden");
}
}
},
// Deactivate all the dropdown toggles when another one is active
deactivateAllDropdownToggles: function() {
var activeDropdownToggles = document.querySelectorAll("[data-toggle='dropdown'].active");
for (var i = 0; i < activeDropdownToggles.length; i++) {
activeDropdownToggles[i].classList.remove("active");
activeDropdownToggles[i].closest(".dropdown").classList.remove("show");
}
},
// Toggle modal (using Javascript)
toggleModal: function(modalId) {
var modal = document.getElementById(modalId);
if (modal) {
modal.classList.toggle("show");
}
},
/* Code block for handling sticky alerts */
// Make an ID for an element
makeId: function(length) {
var result = "";
var characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var charactersLength = characters.length;
for ( var i = 0; i < length; i++ ) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
},
// Toast an alert (show, fade, dispose)
toastAlert: function(alertId, timeShown) {
var alertElement = document.getElementById(alertId);
// Setting the default timeShown
if (timeShown === undefined) {
timeShown = 5000;
}
// Alert is only toasted if it does not have the .show class
if (!alertElement.classList.contains("show")) {
// Add .alert-block class if it does not exist
if (!alertElement.classList.contains("alert-block")) {
alertElement.classList.add("alert-block");
}
// Show the alert
// The 0.25 seconds delay is for the animation
setTimeout(function() {
alertElement.classList.add("show");
}, 250);
// Wait some time (timeShown + 250) and fade out the alert
var timeToFade = timeShown + 250;
setTimeout(function() {
alertElement.classList.add("fade");
}, timeToFade);
// Wait some more time (timeToFade + 500) and dispose the alert (by removing the .alert-block class)
// Again, the extra delay is for the animation
// Remove the .show and .fade classes (so the alert can be toasted again)
var timeToDestroy = timeToFade + 500;
setTimeout(function() {
alertElement.classList.remove("alert-block");
alertElement.classList.remove("show");
alertElement.classList.remove("fade");
}, timeToDestroy);
}
},
// Create a sticky alert, display it, and then remove it
initStickyAlert: function(param) {
// Setting the variables from the param
var content = ("content" in param) ? param.content: "";
var title = ("title" in param) ? param.title: "";
var alertType = ("alertType" in param) ? param.alertType: "";
var fillType = ("fillType" in param) ? param.fillType: "";
var hasDismissButton = ("hasDismissButton" in param) ? param.hasDismissButton: true;
var timeShown = ("timeShown" in param) ? param.timeShown: 5000;
// Create the alert element
var alertElement = document.createElement("div");
// Set ID to the alert element
alertElement.setAttribute("id", this.makeId(6));
// Add the title
if (title) {
content = "<h4 class='alert-heading'>" + title + "</h4>" + content;
}
// Add the classes to the alert element
alertElement.classList.add("alert");
if (alertType) {
alertElement.classList.add(alertType);
}
if (fillType) {
alertElement.classList.add(fillType);
}
// Add the close button to the content (if required)
if (hasDismissButton) {
content = "<button class='close' data-dismiss='alert' type='button' aria-label='Close'><span aria-hidden='true'>&times;</span></button>" + content;
}
// Add the content to the alert element
alertElement.innerHTML = content;
// Append the alert element to the sticky alerts
this.stickyAlerts.insertBefore(alertElement, this.stickyAlerts.childNodes[0]);
// Toast the alert
this.toastAlert(alertElement.getAttribute("id"), timeShown);
},
/* End code block for handling sticky alerts */
// Click handler that can be overridden by users if needed
clickHandler: function(event) {},
// Keydown handler that can be overridden by users if needed
keydownHandler: function(event) {},
}
/* Things done once the DOM is loaded */
function halfmoonOnDOMContentLoaded() {
// Re-initializing the required elements (to avoid issues with virtual DOM)
if (!halfmoon.pageWrapper) {
halfmoon.pageWrapper = document.getElementsByClassName("page-wrapper")[0];
}
if (!halfmoon.stickyAlerts) {
halfmoon.stickyAlerts = document.getElementsByClassName("sticky-alerts")[0];
}
// Handle the cookie and variable for dark mode
// 1. First preference is given to the cookie if it exists
if (halfmoon.readCookie("halfmoon_preferredMode")) {
if (halfmoon.readCookie("halfmoon_preferredMode") == "dark-mode") {
halfmoon.darkModeOn = true;
} else {
halfmoon.darkModeOn = false;
}
} else {
// 2. If cookie does not exist, next preference is for the dark mode setting
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
halfmoon.darkModeOn = true;
} else {
// 3. If all else fails, re-initialize the dark mode preference depending on the .dark-mode class
if (document.body.classList.contains("dark-mode")) {
halfmoon.darkModeOn = true;
} else {
halfmoon.darkModeOn = false;
}
}
}
// Automatically set preferred theme
// But only if one of the data-attribute is provided
if (document.body.getAttribute("data-set-preferred-mode-onload") || document.body.getAttribute("data-set-preferred-theme-onload")) {
if (halfmoon.darkModeOn) {
if (!document.body.classList.contains("dark-mode")) {
document.body.classList.add("dark-mode");
}
} else {
if (document.body.classList.contains("dark-mode")) {
document.body.classList.remove("dark-mode");
}
}
}
// Hiding sidebar on first load on small screens (unless data-attribute provided)
// Or on larger screens when sidebar type is overlayed-all
if (document.documentElement.clientWidth <= 768) {
if (halfmoon.pageWrapper) {
if (!halfmoon.pageWrapper.getAttribute("data-show-sidebar-onload-sm-and-down")) {
halfmoon.pageWrapper.setAttribute("data-sidebar-hidden", "hidden");
}
}
} else {
if (halfmoon.pageWrapper) {
if (halfmoon.pageWrapper.getAttribute("data-sidebar-type") === "overlayed-all") {
halfmoon.pageWrapper.setAttribute("data-sidebar-hidden", "hidden");
}
}
}
// Adding the click event listener
document.addEventListener(
"click",
function(event) {
var eventCopy = event;
var target = event.target;
// Handle clicks on dropdown toggles
if (target.matches("[data-toggle='dropdown']") || target.matches("[data-toggle='dropdown'] *")) {
if (target.matches("[data-toggle='dropdown'] *")) {
target = target.closest("[data-toggle='dropdown']");
}
if (target.classList.contains("active")) {
target.classList.remove("active");
target.closest(".dropdown").classList.remove("show");
} else {
halfmoon.deactivateAllDropdownToggles();
target.classList.add("active");
target.closest(".dropdown").classList.add("show");
}
} else {
if (!target.matches(".dropdown-menu *")) {
halfmoon.deactivateAllDropdownToggles();
}
}
// Handle clicks on alert dismiss buttons
if (target.matches(".alert [data-dismiss='alert']") || target.matches(".alert [data-dismiss='alert'] *")) {
if (target.matches(".alert [data-dismiss='alert'] *")) {
target = target.closest(".alert [data-dismiss='alert']");
}
target.parentNode.classList.add("dispose");
}
// Handle clicks on modal toggles
if (target.matches("[data-toggle='modal']") || target.matches("[data-toggle='modal'] *")) {
if (target.matches("[data-toggle='modal'] *")) {
target = target.closest("[data-toggle='modal']");
}
var targetModal = document.getElementById(target.getAttribute("data-target"));
if (targetModal) {
if (targetModal.classList.contains("modal")) {
halfmoon.toggleModal(target.getAttribute("data-target"));
}
}
}
// Handle clicks on modal dismiss buttons
if (target.matches(".modal [data-dismiss='modal']") || target.matches(".modal [data-dismiss='modal'] *")) {
if (target.matches(".modal [data-dismiss='modal'] *")) {
target = target.closest(".modal [data-dismiss='modal']");
}
target.closest(".modal").classList.remove("show");
}
// Handle clicks on modal overlays
if (target.matches(".modal-dialog")) {
var parentModal = target.closest(".modal");
if (!parentModal.getAttribute("data-overlay-dismissal-disabled")) {
if (parentModal.classList.contains("show")) {
parentModal.classList.remove("show");
} else {
window.location.hash = "#";
}
}
}
// Call the click handler method to handle any logic set by the user in their projects to handle clicks
halfmoon.clickHandler(eventCopy);
},
false
);
// Adding the key down event listener (for shortcuts and accessibility)
document.addEventListener(
"keydown",
function(event) {
var eventCopy = event;
// Shortcuts are triggered only if no input, textarea, or select has focus,
// If the control key or command key is not pressed down,
// And if the enabling data attribute is present on the DOM's body
if (!(document.querySelector("input:focus") || document.querySelector("textarea:focus") || document.querySelector("select:focus"))) {
event = event || window.event;
if (!(event.ctrlKey || event.metaKey)) {
// Toggle sidebar when [shift] + [S] keys are pressed
if (document.body.getAttribute("data-sidebar-shortcut-enabled")) {
if (event.shiftKey && event.which == 83) {
// Variable to store whether a modal is open or not
var modalOpen = false;
// Hash exists, so we check if it belongs to a modal
if (window.location.hash) {
var hash = window.location.hash.substring(1);
var elem = document.getElementById(hash);
if (elem) {
if (elem.classList.contains("modal")) {
modalOpen = true;
}
}
}
// Check for a modal with the .show class
if (document.querySelector(".modal.show")) {
modalOpen = true;
}
// This shortcut works only if no modal is open
if (!modalOpen) {
halfmoon.toggleSidebar();
event.preventDefault();
}
}
}
// Toggle dark mode when [shift] + [D] keys are pressed
if (document.body.getAttribute("data-dm-shortcut-enabled")) {
if (event.shiftKey && event.which == 68) {
halfmoon.toggleDarkMode();
event.preventDefault();
}
}
}
}
// Handling other keydown events
if (event.which === 27) {
// Close dropdown menu (if one is open) when [esc] key is pressed
if (document.querySelector("[data-toggle='dropdown'].active")) {
var elem = document.querySelector("[data-toggle='dropdown'].active");
elem.classList.remove("active");
elem.closest(".dropdown").classList.remove("show");
event.preventDefault();
}
// Close modal (if one is open, and if no dropdown menu is open) when [esc] key is pressed
// Conditional on dropdowns so that dropdowns on modals can be closed with the keyboard without closing the modal
else {
// Hash exists, so we check if it belongs to a modal
if (window.location.hash) {
var hash = window.location.hash.substring(1);
var elem = document.getElementById(hash);
if (elem) {
if (elem.classList.contains("modal")) {
if (!elem.getAttribute("data-esc-dismissal-disabled")) {
window.location.hash = "#";
event.preventDefault();
}
}
}
}
// Check for a modal with the .show class
if (document.querySelector(".modal.show")) {
var elem = document.querySelector(".modal.show");
if (!elem.getAttribute("data-esc-dismissal-disabled")) {
elem.classList.remove("show");
event.preventDefault();
}
}
}
}
// Call the keydown handler method to handle any logic set by the user in their projects to handle keydown events
halfmoon.keydownHandler(eventCopy);
}
);
// Handling custom file inputs
var halfmoonCustomFileInputs = document.querySelectorAll(".custom-file input");
for (var i = 0; i < halfmoonCustomFileInputs.length; i++) {
var customFile = halfmoonCustomFileInputs[i];
// Create file name container element, add the class name, and set default value
// Append it to the custom file element
var fileNamesContainer = document.createElement("div");
fileNamesContainer.classList.add("file-names");
var dataDefaultValue = customFile.getAttribute("data-default-value");
if (dataDefaultValue) {
fileNamesContainer.innerHTML = dataDefaultValue;
} else {
fileNamesContainer.innerHTML = "No file chosen";
}
customFile.parentNode.appendChild(fileNamesContainer);
// Add the event listener that will update the contents of the file name container element on change
customFile.addEventListener(
"change",
function(event) {
fileNamesContainer = event.target.parentNode.querySelector(".file-names");
if (event.target.files.length === 1) {
fileNamesContainer.innerHTML = event.target.files[0].name;
} else if (event.target.files.length > 1) {
fileNamesContainer.innerHTML = event.target.files.length + " files";
} else {
fileNamesContainer.innerHTML = "No file chosen";
}
}
);
}
// Adding the .with-transitions class to the page-wrapper so that transitions are enabled
// This way, the weird bug on Chrome is avoided, where the transitions run on load
if (halfmoon.pageWrapper) {
halfmoon.pageWrapper.classList.add("with-transitions");
}
}
// Add the halfmoonOnDOMContentLoaded to the main halfmoon object
// And export the halfmoon object as a module
halfmoon.onDOMContentLoaded = halfmoonOnDOMContentLoaded;
module.exports = halfmoon;

View File

@@ -0,0 +1,511 @@
/*
* -----------------------------------------------------------------------------
* Halfmoon JS
* Version: 1.1.1
* https://www.gethalfmoon.com
* Copyright, Halfmoon UI
* Licensed under MIT (https://www.gethalfmoon.com/license)
* -----------------------------------------------------------------------------
* The above notice must be included in its entirety when this file is used.
*/
/* Start polyfills */
// Polyfill for Element.matches()
if (!Element.prototype.matches) {
Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;
}
// Polyfill for Element.closest()
if (!Element.prototype.closest) {
Element.prototype.closest = function(s) {
var el = this;
do {
if (el.matches(s)) return el;
el = el.parentElement || el.parentNode;
} while (el !== null && el.nodeType === 1);
return null;
};
}
// Polyfill for Element.classList (http://purl.eligrey.com/github/classList.js/blob/master/classList.js)
"document"in self&&("classList"in document.createElement("_")&&(!document.createElementNS||"classList"in document.createElementNS("http://www.w3.org/2000/svg","g"))||!function(t){"use strict";if("Element"in t){var e="classList",n="prototype",i=t.Element[n],s=Object,r=String[n].trim||function(){return this.replace(/^\s+|\s+$/g,"")},o=Array[n].indexOf||function(t){for(var e=0,n=this.length;n>e;e++)if(e in this&&this[e]===t)return e;return-1},c=function(t,e){this.name=t,this.code=DOMException[t],this.message=e},a=function(t,e){if(""===e)throw new c("SYNTAX_ERR","The token must not be empty.");if(/\s/.test(e))throw new c("INVALID_CHARACTER_ERR","The token must not contain space characters.");return o.call(t,e)},l=function(t){for(var e=r.call(t.getAttribute("class")||""),n=e?e.split(/\s+/):[],i=0,s=n.length;s>i;i++)this.push(n[i]);this._updateClassName=function(){t.setAttribute("class",this.toString())}},u=l[n]=[],h=function(){return new l(this)};if(c[n]=Error[n],u.item=function(t){return this[t]||null},u.contains=function(t){return~a(this,t+"")},u.add=function(){var t,e=arguments,n=0,i=e.length,s=!1;do t=e[n]+"",~a(this,t)||(this.push(t),s=!0);while(++n<i);s&&this._updateClassName()},u.remove=function(){var t,e,n=arguments,i=0,s=n.length,r=!1;do for(t=n[i]+"",e=a(this,t);~e;)this.splice(e,1),r=!0,e=a(this,t);while(++i<s);r&&this._updateClassName()},u.toggle=function(t,e){var n=this.contains(t),i=n?e!==!0&&"remove":e!==!1&&"add";return i&&this[i](t),e===!0||e===!1?e:!n},u.replace=function(t,e){var n=a(t+"");~n&&(this.splice(n,1,e),this._updateClassName())},u.toString=function(){return this.join(" ")},s.defineProperty){var f={get:h,enumerable:!0,configurable:!0};try{s.defineProperty(i,e,f)}catch(p){void 0!==p.number&&-2146823252!==p.number||(f.enumerable=!1,s.defineProperty(i,e,f))}}else s[n].__defineGetter__&&i.__defineGetter__(e,h)}}(self),function(){"use strict";var t=document.createElement("_");if(t.classList.add("c1","c2"),!t.classList.contains("c2")){var e=function(t){var e=DOMTokenList.prototype[t];DOMTokenList.prototype[t]=function(t){var n,i=arguments.length;for(n=0;i>n;n++)t=arguments[n],e.call(this,t)}};e("add"),e("remove")}if(t.classList.toggle("c3",!1),t.classList.contains("c3")){var n=DOMTokenList.prototype.toggle;DOMTokenList.prototype.toggle=function(t,e){return 1 in arguments&&!this.contains(t)==!e?e:n.call(this,t)}}"replace"in document.createElement("_").classList||(DOMTokenList.prototype.replace=function(t,e){var n=this.toString().split(" "),i=n.indexOf(t+"");~i&&(n=n.slice(i),this.remove.apply(this,n),this.add(e),this.add.apply(this,n.slice(1)))}),t=null}());
/* End polyfills */
/* Halfmoon JS core */
var halfmoon = {
// Getting the required elements
// Re-initialized once the DOM is loaded (to avoid issues with virtual DOM)
pageWrapper: document.getElementsByClassName("page-wrapper")[0],
stickyAlerts: document.getElementsByClassName("sticky-alerts")[0],
darkModeOn: false, // Also re-initialized once the DOM is loaded (see below)
// Create cookie
createCookie: function(name, value, days) {
var expires;
if (days) {
var date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
expires = "; expires=" + date.toGMTString();
}
else {
expires = "";
}
document.cookie = name + "=" + value + expires + "; path=/";
},
// Read cookie
readCookie: function(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(";");
for(var i=0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) === " ") {
c = c.substring(1, c.length);
}
if (c.indexOf(nameEQ) === 0) {
return c.substring(nameEQ.length,c.length);
}
}
return null;
},
// Erase cookie
eraseCookie: function(name) {
this.createCookie(name, "", -1);
},
// Toggle light/dark mode
toggleDarkMode: function() {
if (document.body.classList.contains("dark-mode")) {
document.body.classList.remove("dark-mode");
this.darkModeOn = false;
this.createCookie("halfmoon_preferredMode", "light-mode", 365);
} else {
document.body.classList.add("dark-mode");
this.darkModeOn = true;
this.createCookie("halfmoon_preferredMode", "dark-mode", 365);
}
},
// Get preferred mode
getPreferredMode: function() {
if (this.readCookie("halfmoon_preferredMode")) {
return this.readCookie("halfmoon_preferredMode");
} else {
return "not-set";
}
},
// Toggles sidebar
toggleSidebar: function() {
if (this.pageWrapper) {
if (this.pageWrapper.getAttribute("data-sidebar-hidden")) {
this.pageWrapper.removeAttribute("data-sidebar-hidden");
} else {
this.pageWrapper.setAttribute("data-sidebar-hidden", "hidden");
}
}
},
// Deactivate all the dropdown toggles when another one is active
deactivateAllDropdownToggles: function() {
var activeDropdownToggles = document.querySelectorAll("[data-toggle='dropdown'].active");
for (var i = 0; i < activeDropdownToggles.length; i++) {
activeDropdownToggles[i].classList.remove("active");
activeDropdownToggles[i].closest(".dropdown").classList.remove("show");
}
},
// Toggle modal (using Javascript)
toggleModal: function(modalId) {
var modal = document.getElementById(modalId);
if (modal) {
modal.classList.toggle("show");
}
},
/* Code block for handling sticky alerts */
// Make an ID for an element
makeId: function(length) {
var result = "";
var characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
var charactersLength = characters.length;
for ( var i = 0; i < length; i++ ) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
},
// Toast an alert (show, fade, dispose)
toastAlert: function(alertId, timeShown) {
var alertElement = document.getElementById(alertId);
// Setting the default timeShown
if (timeShown === undefined) {
timeShown = 5000;
}
// Alert is only toasted if it does not have the .show class
if (!alertElement.classList.contains("show")) {
// Add .alert-block class if it does not exist
if (!alertElement.classList.contains("alert-block")) {
alertElement.classList.add("alert-block");
}
// Show the alert
// The 0.25 seconds delay is for the animation
setTimeout(function() {
alertElement.classList.add("show");
}, 250);
// Wait some time (timeShown + 250) and fade out the alert
var timeToFade = timeShown + 250;
setTimeout(function() {
alertElement.classList.add("fade");
}, timeToFade);
// Wait some more time (timeToFade + 500) and dispose the alert (by removing the .alert-block class)
// Again, the extra delay is for the animation
// Remove the .show and .fade classes (so the alert can be toasted again)
var timeToDestroy = timeToFade + 500;
setTimeout(function() {
alertElement.classList.remove("alert-block");
alertElement.classList.remove("show");
alertElement.classList.remove("fade");
}, timeToDestroy);
}
},
// Create a sticky alert, display it, and then remove it
initStickyAlert: function(param) {
// Setting the variables from the param
var content = ("content" in param) ? param.content: "";
var title = ("title" in param) ? param.title: "";
var alertType = ("alertType" in param) ? param.alertType: "";
var fillType = ("fillType" in param) ? param.fillType: "";
var hasDismissButton = ("hasDismissButton" in param) ? param.hasDismissButton: true;
var timeShown = ("timeShown" in param) ? param.timeShown: 5000;
// Create the alert element
var alertElement = document.createElement("div");
// Set ID to the alert element
alertElement.setAttribute("id", this.makeId(6));
// Add the title
if (title) {
content = "<h4 class='alert-heading'>" + title + "</h4>" + content;
}
// Add the classes to the alert element
alertElement.classList.add("alert");
if (alertType) {
alertElement.classList.add(alertType);
}
if (fillType) {
alertElement.classList.add(fillType);
}
// Add the close button to the content (if required)
if (hasDismissButton) {
content = "<button class='close' data-dismiss='alert' type='button' aria-label='Close'><span aria-hidden='true'>&times;</span></button>" + content;
}
// Add the content to the alert element
alertElement.innerHTML = content;
// Append the alert element to the sticky alerts
this.stickyAlerts.insertBefore(alertElement, this.stickyAlerts.childNodes[0]);
// Toast the alert
this.toastAlert(alertElement.getAttribute("id"), timeShown);
},
/* End code block for handling sticky alerts */
// Click handler that can be overridden by users if needed
clickHandler: function(event) {},
// Keydown handler that can be overridden by users if needed
keydownHandler: function(event) {},
}
/* Things done once the DOM is loaded */
function halfmoonOnDOMContentLoaded() {
// Re-initializing the required elements (to avoid issues with virtual DOM)
if (!halfmoon.pageWrapper) {
halfmoon.pageWrapper = document.getElementsByClassName("page-wrapper")[0];
}
if (!halfmoon.stickyAlerts) {
halfmoon.stickyAlerts = document.getElementsByClassName("sticky-alerts")[0];
}
// Handle the cookie and variable for dark mode
// 1. First preference is given to the cookie if it exists
if (halfmoon.readCookie("halfmoon_preferredMode")) {
if (halfmoon.readCookie("halfmoon_preferredMode") == "dark-mode") {
halfmoon.darkModeOn = true;
} else {
halfmoon.darkModeOn = false;
}
} else {
// 2. If cookie does not exist, next preference is for the dark mode setting
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
halfmoon.darkModeOn = true;
} else {
// 3. If all else fails, re-initialize the dark mode preference depending on the .dark-mode class
if (document.body.classList.contains("dark-mode")) {
halfmoon.darkModeOn = true;
} else {
halfmoon.darkModeOn = false;
}
}
}
// Automatically set preferred theme
// But only if one of the data-attribute is provided
if (document.body.getAttribute("data-set-preferred-mode-onload") || document.body.getAttribute("data-set-preferred-theme-onload")) {
if (halfmoon.darkModeOn) {
if (!document.body.classList.contains("dark-mode")) {
document.body.classList.add("dark-mode");
}
} else {
if (document.body.classList.contains("dark-mode")) {
document.body.classList.remove("dark-mode");
}
}
}
// Hiding sidebar on first load on small screens (unless data-attribute provided)
// Or on larger screens when sidebar type is overlayed-all
if (document.documentElement.clientWidth <= 768) {
if (halfmoon.pageWrapper) {
if (!halfmoon.pageWrapper.getAttribute("data-show-sidebar-onload-sm-and-down")) {
halfmoon.pageWrapper.setAttribute("data-sidebar-hidden", "hidden");
}
}
} else {
if (halfmoon.pageWrapper) {
if (halfmoon.pageWrapper.getAttribute("data-sidebar-type") === "overlayed-all") {
halfmoon.pageWrapper.setAttribute("data-sidebar-hidden", "hidden");
}
}
}
// Adding the click event listener
document.addEventListener(
"click",
function(event) {
var eventCopy = event;
var target = event.target;
// Handle clicks on dropdown toggles
if (target.matches("[data-toggle='dropdown']") || target.matches("[data-toggle='dropdown'] *")) {
if (target.matches("[data-toggle='dropdown'] *")) {
target = target.closest("[data-toggle='dropdown']");
}
if (target.classList.contains("active")) {
target.classList.remove("active");
target.closest(".dropdown").classList.remove("show");
} else {
halfmoon.deactivateAllDropdownToggles();
target.classList.add("active");
target.closest(".dropdown").classList.add("show");
}
} else {
if (!target.matches(".dropdown-menu *")) {
halfmoon.deactivateAllDropdownToggles();
}
}
// Handle clicks on alert dismiss buttons
if (target.matches(".alert [data-dismiss='alert']") || target.matches(".alert [data-dismiss='alert'] *")) {
if (target.matches(".alert [data-dismiss='alert'] *")) {
target = target.closest(".alert [data-dismiss='alert']");
}
target.parentNode.classList.add("dispose");
}
// Handle clicks on modal toggles
if (target.matches("[data-toggle='modal']") || target.matches("[data-toggle='modal'] *")) {
if (target.matches("[data-toggle='modal'] *")) {
target = target.closest("[data-toggle='modal']");
}
var targetModal = document.getElementById(target.getAttribute("data-target"));
if (targetModal) {
if (targetModal.classList.contains("modal")) {
halfmoon.toggleModal(target.getAttribute("data-target"));
}
}
}
// Handle clicks on modal dismiss buttons
if (target.matches(".modal [data-dismiss='modal']") || target.matches(".modal [data-dismiss='modal'] *")) {
if (target.matches(".modal [data-dismiss='modal'] *")) {
target = target.closest(".modal [data-dismiss='modal']");
}
target.closest(".modal").classList.remove("show");
}
// Handle clicks on modal overlays
if (target.matches(".modal-dialog")) {
var parentModal = target.closest(".modal");
if (!parentModal.getAttribute("data-overlay-dismissal-disabled")) {
if (parentModal.classList.contains("show")) {
parentModal.classList.remove("show");
} else {
window.location.hash = "#";
}
}
}
// Call the click handler method to handle any logic set by the user in their projects to handle clicks
halfmoon.clickHandler(eventCopy);
},
false
);
// Adding the key down event listener (for shortcuts and accessibility)
document.addEventListener(
"keydown",
function(event) {
var eventCopy = event;
// Shortcuts are triggered only if no input, textarea, or select has focus,
// If the control key or command key is not pressed down,
// And if the enabling data attribute is present on the DOM's body
if (!(document.querySelector("input:focus") || document.querySelector("textarea:focus") || document.querySelector("select:focus"))) {
event = event || window.event;
if (!(event.ctrlKey || event.metaKey)) {
// Toggle sidebar when [shift] + [S] keys are pressed
if (document.body.getAttribute("data-sidebar-shortcut-enabled")) {
if (event.shiftKey && event.which == 83) {
// Variable to store whether a modal is open or not
var modalOpen = false;
// Hash exists, so we check if it belongs to a modal
if (window.location.hash) {
var hash = window.location.hash.substring(1);
var elem = document.getElementById(hash);
if (elem) {
if (elem.classList.contains("modal")) {
modalOpen = true;
}
}
}
// Check for a modal with the .show class
if (document.querySelector(".modal.show")) {
modalOpen = true;
}
// This shortcut works only if no modal is open
if (!modalOpen) {
halfmoon.toggleSidebar();
event.preventDefault();
}
}
}
// Toggle dark mode when [shift] + [D] keys are pressed
if (document.body.getAttribute("data-dm-shortcut-enabled")) {
if (event.shiftKey && event.which == 68) {
halfmoon.toggleDarkMode();
event.preventDefault();
}
}
}
}
// Handling other keydown events
if (event.which === 27) {
// Close dropdown menu (if one is open) when [esc] key is pressed
if (document.querySelector("[data-toggle='dropdown'].active")) {
var elem = document.querySelector("[data-toggle='dropdown'].active");
elem.classList.remove("active");
elem.closest(".dropdown").classList.remove("show");
event.preventDefault();
}
// Close modal (if one is open, and if no dropdown menu is open) when [esc] key is pressed
// Conditional on dropdowns so that dropdowns on modals can be closed with the keyboard without closing the modal
else {
// Hash exists, so we check if it belongs to a modal
if (window.location.hash) {
var hash = window.location.hash.substring(1);
var elem = document.getElementById(hash);
if (elem) {
if (elem.classList.contains("modal")) {
if (!elem.getAttribute("data-esc-dismissal-disabled")) {
window.location.hash = "#";
event.preventDefault();
}
}
}
}
// Check for a modal with the .show class
if (document.querySelector(".modal.show")) {
var elem = document.querySelector(".modal.show");
if (!elem.getAttribute("data-esc-dismissal-disabled")) {
elem.classList.remove("show");
event.preventDefault();
}
}
}
}
// Call the keydown handler method to handle any logic set by the user in their projects to handle keydown events
halfmoon.keydownHandler(eventCopy);
}
);
// Handling custom file inputs
var halfmoonCustomFileInputs = document.querySelectorAll(".custom-file input");
for (var i = 0; i < halfmoonCustomFileInputs.length; i++) {
var customFile = halfmoonCustomFileInputs[i];
// Create file name container element, add the class name, and set default value
// Append it to the custom file element
var fileNamesContainer = document.createElement("div");
fileNamesContainer.classList.add("file-names");
var dataDefaultValue = customFile.getAttribute("data-default-value");
if (dataDefaultValue) {
fileNamesContainer.innerHTML = dataDefaultValue;
} else {
fileNamesContainer.innerHTML = "No file chosen";
}
customFile.parentNode.appendChild(fileNamesContainer);
// Add the event listener that will update the contents of the file name container element on change
customFile.addEventListener(
"change",
function(event) {
fileNamesContainer = event.target.parentNode.querySelector(".file-names");
if (event.target.files.length === 1) {
fileNamesContainer.innerHTML = event.target.files[0].name;
} else if (event.target.files.length > 1) {
fileNamesContainer.innerHTML = event.target.files.length + " files";
} else {
fileNamesContainer.innerHTML = "No file chosen";
}
}
);
}
// Adding the .with-transitions class to the page-wrapper so that transitions are enabled
// This way, the weird bug on Chrome is avoided, where the transitions run on load
if (halfmoon.pageWrapper) {
halfmoon.pageWrapper.classList.add("with-transitions");
}
}
// Call the function when the DOM is loaded
document.addEventListener("DOMContentLoaded", halfmoonOnDOMContentLoaded);

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,31 @@
{
"name": "halfmoon",
"version": "1.1.1",
"description": "Front-end framework with a built-in dark mode and full customizability using CSS variables; great for building dashboards and tools",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/halfmoonui/halfmoon.git"
},
"keywords": [
"halfmoon",
"css",
"javascript",
"dark-theme",
"dark-mode",
"dashboard",
"web-framework",
"css-framework",
"css-variables",
"css-custom-properties"
],
"author": "Halfmoon UI",
"license": "MIT",
"bugs": {
"url": "https://github.com/halfmoonui/halfmoon/issues"
},
"homepage": "https://github.com/halfmoonui/halfmoon#readme"
}

View File

@@ -0,0 +1,92 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Meta tags -->
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport" />
<meta name="viewport" content="width=device-width" />
<!-- Favicon and title -->
<link rel="icon" href="path/to/fav.png">
<title>Starter template - Halfmoon</title>
<!-- Halfmoon CSS -->
<link href="css/halfmoon-variables.min.css" rel="stylesheet" />
<!--
Or,
Use the following (no variables, supports IE11):
<link href="css/halfmoon.min.css" rel="stylesheet" />
-->
</head>
<body class="with-custom-webkit-scrollbars with-custom-css-scrollbars" data-dm-shortcut-enabled="true" data-sidebar-shortcut-enabled="true" data-set-preferred-theme-onload="true">
<!-- Modals go here -->
<!-- Reference: https://www.gethalfmoon.com/docs/modal -->
<!-- Page wrapper start -->
<div class="page-wrapper with-navbar with-sidebar with-navbar-fixed-bottom" data-sidebar-type="overlayed-sm-and-down">
<!-- Sticky alerts (toasts), empty container -->
<!-- Reference: https://www.gethalfmoon.com/docs/sticky-alerts-toasts -->
<div class="sticky-alerts"></div>
<!-- Navbar start -->
<nav class="navbar">
<!-- Reference: https://www.gethalfmoon.com/docs/navbar -->
</nav>
<!-- Navbar end -->
<!-- Sidebar overlay -->
<div class="sidebar-overlay" onclick="halfmoon.toggleSidebar()"></div>
<!-- Sidebar start -->
<div class="sidebar">
<!-- Reference: https://www.gethalfmoon.com/docs/sidebar -->
</div>
<!-- Sidebar end -->
<!-- Content wrapper start -->
<div class="content-wrapper">
<!--
Add your page's main content here
Examples:
1. https://www.gethalfmoon.com/docs/content-and-cards/#building-a-page
2. https://www.gethalfmoon.com/docs/grid-system/#building-a-dashboard
-->
<div class="w-full h-full d-flex align-items-center justify-content-center">
<div class="content">
<h1 class="content-title">Halfmoon starter template</h1>
<div>
<a href="https://www.gethalfmoon.com" class="btn btn-link px-0">Halfmoon website</a>
</div>
<div>
<a href="https://www.gethalfmoon.com/docs" class="btn btn-link px-0">Halfmoon docs</a>
</div>
<div>
<a href="https://www.gethalfmoon.com/docs/page-building/#starter-template-generator" class="btn btn-link px-0">Starter template generator</a>
</div>
<div>
<a href="https://www.twitter.com/halfmoonui" class="btn btn-link px-0">Follow on Twitter for updates</a>
</div>
<div class="mt-20">
Toggles:
<button class="btn btn-sm" type="button" onclick="halfmoon.toggleDarkMode()">Dark mode</button>
<button class="btn btn-sm" type="button" onclick="halfmoon.toggleSidebar()">Sidebar</button>
</div>
</div>
</div>
</div>
<!-- Content wrapper end -->
<!-- Navbar fixed bottom start -->
<nav class="navbar navbar-fixed-bottom">
<!-- Reference: https://www.gethalfmoon.com/docs/navbar#navbar-fixed-bottom -->
</nav>
<!-- Navbar fixed bottom end -->
</div>
<!-- Page wrapper end -->
<!-- Halfmoon JS -->
<script src="js/halfmoon.min.js"></script>
</body>
</html>

View File

@@ -1,2 +1,2 @@
# Quantum Font
100% font made by [sesohq](https://www.sesohq.com/) over at [sesohq.sellfy.store](https://sesohq.sellfy.store/p/3enu/).
100% free font made by [sesohq](https://www.sesohq.com/) over at [sesohq.sellfy.store](https://sesohq.sellfy.store/p/3enu/).

View File

@@ -1,4 +1,4 @@
User-agent: *
Allow: /
Sitemap: https://nibblepoker.lu/sitemap.xml
Sitemap: https://nibblepoker.lu/sitemap.txt