refactored sitekey routes

This commit is contained in:
realaravinth
2021-05-05 12:57:05 +05:30
parent f0254b3b77
commit 6069962d3e
24 changed files with 48 additions and 44 deletions

View File

@@ -0,0 +1,36 @@
<fieldset class="sitekey__level-container" id="level-group-<.= level .>">
<legend class="sitekey__level-title">
Level <.= level .>
</legend>
<label class="sitekey-form__level-label" for="visitor<.= level .>"
>Visitor
<input
class="sitekey-form__level-input"
type="number"
name=""
value=""
id="visitor<.= level .>"
/>
</label>
<label class="sitekey-form__level-label" for="difficulty<.= level .>">
Difficulty
<input
type="number"
name="difficulty"
class="sitekey-form__level-input"
value=""
id="difficulty<.= level .>"
/>
</label>
<label class="sitekey-form__level-label--hidden" for="add">
Add level
<input
class="sitekey-form__level-add-level-button"
type="button"
name="add"
id="add"
value="Add"
/>
</label>
</fieldset>

View File

@@ -0,0 +1,117 @@
/*
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import validateLevel from './levels/validateLevel';
import getNumLevels from './levels/getNumLevels';
import {LEVELS} from './levels/';
import * as UpdateLevel from './levels/updateLevel';
import {
getRemoveButtonHTML,
addRemoveLevelButtonEventListener,
} from './removeLevelButton';
import CONST from './const';
const ADD_LEVEL_BUTTON = 'sitekey-form__level-add-level-button';
/**
* Gets executed when 'Add' Button is clicked to add levels
* Used to validate levels per m_captcha::defense::Defense's
* specifications
*/
const addLevel = (e: Event) => {
const eventTarget = <HTMLElement>e.target;
const PARENT = <HTMLLabelElement>eventTarget.parentElement;
const FIELDSET = <HTMLElement>PARENT.parentElement;
const onScreenLevel = getNumLevels();
const isValid = validateLevel(onScreenLevel);
console.log(`[addLevelButton] isValid: ${isValid}`);
if (!isValid) {
return console.error('Aborting level addition');
}
eventTarget.remove();
PARENT.innerHTML = getRemoveButtonHTML(onScreenLevel);
PARENT.htmlFor = `${CONST.REMOVE_LEVEL_BUTTON_ID_WITHOUT_LEVEL}${onScreenLevel}`;
//FIELDSET.innerHTML += getRemoveButtonHTML(numLevels);
addRemoveLevelButtonEventListener(onScreenLevel);
//PARENT.remove();
const newLevelHTML = getHtml(onScreenLevel + 1);
FIELDSET.insertAdjacentHTML('afterend', newLevelHTML);
UpdateLevel.register(onScreenLevel);
addLevelButtonAddEventListener();
};
/** adds onclick event listener */
const addLevelButtonAddEventListener = () => {
let addLevelButton = <HTMLElement>(
document.querySelector(`.${ADD_LEVEL_BUTTON}`)
);
addLevelButton.addEventListener('click', addLevel);
};
/**
* Generate HTML to be added when 'Add Level' button is clicked
* Check if './add-level.html` to see if this is up to date
*/
const getHtml = (level: number) => {
console.debug(`[generating HTML getHtml]level: ${level}`);
const HTML = `
<fieldset class="sitekey__level-container" id="level-group-${level}">
<legend class="sitekey__level-title">
Level ${level}
</legend>
<label class="sitekey-form__level-label" for="visitor${level}"
>Visitor
<input
class="sitekey-form__level-input"
type="number"
name=""
value=""
id="visitor${level}"
/>
</label>
<label class="sitekey-form__level-label" for="difficulty${level}">
Difficulty
<input
type="number"
name="difficulty"
class="sitekey-form__level-input"
value=""
id="difficulty${level}"
/>
</label>
<label class="sitekey-form__level-label--hidden" for="add">
Add level
<input
class="sitekey-form__level-add-level-button"
type="button"
name="add"
id="add"
value="Add"
/>
</label>
</fieldset>
`;
return HTML;
};
export default addLevelButtonAddEventListener;

View File

@@ -0,0 +1,48 @@
/*
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
const LABEL_INNER_TEXT_WITHOUT_LEVEL = 'Level ';
const LABEL_CLASS = 'sitekey-form__level-label';
const INPUT_ID_WITHOUT_LEVEL = 'level';
const LEVEL_INPUT_CLASS = 'sitekey-form__level-input';
const VISITOR_WITHOUT_LEVEL = 'visitor';
const DIFFICULTY_WITHOUT_LEVEL = 'difficulty';
const LEVEL_CONTAINER_CLASS = 'sitekey__level-container';
const LEVEL_FIELDSET_ID_WITHOUT_LEVEL = 'level-group-';
const LEGEND_CLASS = 'sitekey__level-title';
const REMOVE_LEVEL_BUTTON_ID_WITHOUT_LEVEL = 'remove-level';
const REMOVE_LEVEL_LABEL_TEXT = "Remove Level";
const CONST = {
LABEL_CLASS,
INPUT_ID_WITHOUT_LEVEL,
LEVEL_INPUT_CLASS,
LABEL_INNER_TEXT_WITHOUT_LEVEL,
VISITOR_WITHOUT_LEVEL,
DIFFICULTY_WITHOUT_LEVEL,
LEVEL_CONTAINER_CLASS,
LEVEL_FIELDSET_ID_WITHOUT_LEVEL,
LEGEND_CLASS,
REMOVE_LEVEL_BUTTON_ID_WITHOUT_LEVEL,
REMOVE_LEVEL_LABEL_TEXT,
};
export default CONST;

View File

@@ -0,0 +1,36 @@
<fieldset class="sitekey__level-container" id="level-group-<.= level .>">
<legend class="sitekey__level-title">
Level <.= level .>
</legend>
<label class="sitekey-form__level-label" for="visitor<.= level .>"
>Visitor
<input
class="sitekey-form__level-input"
type="number"
name=""
value=""
id="visitor<.= level .>"
/>
</label>
<label class="sitekey-form__level-label" for="difficulty<.= level .>">
Difficulty
<input
type="number"
name="difficulty"
class="sitekey-form__level-input"
value=""
id="difficulty<.= level .>"
/>
</label>
<label class="sitekey-form__level-label--hidden" for="remove<.= level .>">
Add level
<input
class="sitekey-form__level-add-level-button"
type="button"
name="remove"
id="remove<.= level .>"
value="Remove"
/>
</label>
</fieldset>

View File

@@ -0,0 +1,40 @@
<form class="sitekey-form" action="<.= crate::V1_API_ROUTES.levels.add .>" method="post">
<h1 class="form__title">
<.= form_title .>
</h1>
<label class="sitekey-form__label" for="description">
Description
<input
class="sitekey-form__input"
type="text"
name="description"
id="description"
required
value="<.= form_description .>"
/>
</label>
<label class="sitekey-form__label" for="duration">
Cooldown Duratoin(in seconds)
<input
class="sitekey-form__input"
type="number"
name="duration"
id="duration"
min=0
required
value="<.= form_duration .>"
/>
</label>
<. for level in 1..=levels { .>
<. if level == levels { .>
<. include!("./add-level.html"); .>
<. } else { .>
<. include!("./existing-level.html"); .>
<. } .>
<. } .>
<button class="sitekey-form__submit" type="submit">Submit</button>
</form>

View File

@@ -0,0 +1,101 @@
/*
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {LEVELS} from './levels';
import isBlankString from '../../../utils/isBlankString';
import getFormUrl from '../../../utils/getFormUrl';
import genJsonPayload from '../../../utils/genJsonPayload';
import isNumber from '../../../utils/isNumber';
import VIEWS from '../../../views/v1/routes';
const SITE_KEY_FORM_CLASS = 'sitekey-form';
const FORM = <HTMLFormElement>document.querySelector(`.${SITE_KEY_FORM_CLASS}`);
//const FORM_SUBMIT_BUTTON_CLASS = "sitekey-form__submit";
//const FORM_SUBMIT_BUTTON = <HTMLButtonElement>document.querySelector(`.${FORM_SUBMIT_BUTTON_CLASS}`);
const addSubmitEventListener = () => {
FORM.addEventListener('submit', submit, true);
};
//const validateLevels = (e: Event) => {
// const numLevels = getNumLevels();
// // check if levels are unique and are in increasing order;
// // also if they are positive
// // also if level input field is accompanied by a "Add Level" button,
// // it shouldn't be used for validation
// for (let levelNum = 1; levelNum < numLevels; levelNum++) {
// const inputID = CONST.INPUT_ID_WITHOUT_LEVEL + levelNum;
// const inputElement = <HTMLInputElement>document.getElementById(inputID);
// const val = inputElement.value;
// const filed = CONST.LABEL_INNER_TEXT_WITHOUT_LEVEL + levelNum;
// isBlankString(val, filed, e);
// }
//};
const validateDescription = (e: Event) => {
const inputElement = <HTMLInputElement>document.getElementById('description');
const val = inputElement.value;
const filed = 'Description';
isBlankString(val, filed, e);
return val;
};
const validateDuration = (e: Event) => {
const duartionElement = <HTMLInputElement>document.getElementById('duration');
const duration = parseInt(duartionElement.value);
if (!isNumber(duration) || Number.isNaN(duration)) {
throw new Error('duration can contain nubers only');
}
if (duration <= 0) {
throw new Error('duration must be greater than zero');
}
return duration;
};
const submit = async (e: Event) => {
e.preventDefault();
const description = validateDescription(e);
const duration = validateDuration(e);
const formUrl = getFormUrl(FORM);
const levels = LEVELS.getLevels();
console.debug(`[form submition]: levels: ${levels}`);
const payload = {
levels: levels,
duration,
description,
};
console.debug(`[form submition] json payload: ${JSON.stringify(payload)}`);
const res = await fetch(formUrl, genJsonPayload(payload));
if (res.ok) {
alert('success');
window.location.assign(VIEWS.sitekey);
} else {
const err = await res.json();
alert(`error: ${err.error}`);
}
};
export default addSubmitEventListener;

View File

@@ -0,0 +1,15 @@
<. include!("../../../components/headers.html"); .> <. include!("../../header/index.html");
.>
<main>
<. include!("../../taskbar/index.html"); .> <.
include!("../../help-banner/index.html"); .>
<!-- Main content container -->
<div class="inner-container">
<!-- Main menu/ important actions roaster -->
<. include!("../add/form.html"); .>
</div>
<!-- end of container -->
</main>
<. include!("../../../components/footers.html"); .>

View File

@@ -0,0 +1,24 @@
/*
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import addLevelButtonAddEventListener from './addLevelButton';
import addSubmitEventListener from './form';
export const index = () => {
addLevelButtonAddEventListener();
addSubmitEventListener();
};

View File

@@ -0,0 +1,57 @@
/*
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {Level} from './index';
import CONST from '../const';
import isNumber from '../../../../utils/isNumber';
/** Fetches level from DOM using the ID passesd and validates */
const getLevelFields = (id: number) => {
console.log(`[getLevelFields]: id: ${id}`);
const visitorID = CONST.VISITOR_WITHOUT_LEVEL + id.toString();
const difficultyID = CONST.DIFFICULTY_WITHOUT_LEVEL + id.toString();
const visitorElement = <HTMLInputElement>document.getElementById(visitorID);
const difficultyElement = <HTMLInputElement>(
document.getElementById(difficultyID)
);
const visitor_threshold = parseInt(visitorElement.value);
const difficulty_factor = parseInt(difficultyElement.value);
if (Number.isNaN(visitor_threshold)) {
throw new Error('visitor can contain nubers only');
}
if (Number.isNaN(difficulty_factor)) {
throw new Error('difficulty can contain nubers only');
}
const level: Level = {
difficulty_factor,
visitor_threshold,
};
console.debug(
`[getLevelFields.ts] visitor: ${visitor_threshold} difficulty: ${difficulty_factor}`,
);
return level;
};
export default getLevelFields;

View File

@@ -0,0 +1,29 @@
/*
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import CONST from '../const';
/** returns number of level input fields currently in DOM */
const getNumLevels = () => {
let numLevels = 0;
document.querySelectorAll(`.${CONST.LEVEL_CONTAINER_CLASS}`).forEach(_ => numLevels++);
console.debug(`[getNumLevels]: numLevels: ${numLevels}`);
return numLevels;
};
export default getNumLevels;

View File

@@ -0,0 +1,165 @@
/*
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import getNumLevels from './getNumLevels';
/** Datatype represenging an mCaptcha level */
export type Level = {
difficulty_factor: number;
visitor_threshold: number;
};
/** Datatype representing a collection of mCaptcha levels */
class Levels {
levels: Array<Level>;
numOnScreen: number;
numRecoreded: number;
constructor() {
this.levels = [];
this.numRecoreded = 0;
this.numOnScreen = getNumLevels();
}
add = (newLevel: Level) => {
console.debug(`[levels/index.ts] levels lenght: ${this.levels.length}`);
if (newLevel.difficulty_factor <= 0) {
throw new Error('Difficulty must be greater than zero');
}
if (newLevel.visitor_threshold <= 0) {
throw new Error('Visitors must be graeter than zero');
}
if (this.levels.length == 0) {
this.levels.push(newLevel);
return true;
}
let msg;
let count = 1;
const validate = (level: Level, newLevel: Level) => {
if (level.visitor_threshold >= newLevel.visitor_threshold) {
msg = `Level: ${newLevel} visitor count has to greater than previous levels. See ${count}`;
return true;
}
if (level.difficulty_factor >= newLevel.difficulty_factor) {
msg = `Level ${this.levels.length} difficulty has to greater than previous levels See ${count}`;
return true;
}
count++;
return false;
};
if (this.levels.find(level => validate(level, newLevel))) {
alert(msg);
throw new Error(msg);
} else {
this.levels.push(newLevel);
this.numOnScreen += 1;
this.numRecoreded += 1;
}
};
get = () => this.levels;
}
/** Singleton that does manipulations on Levels object */
export const LEVELS = (function() {
const levels = new Levels();
return {
/** get levels */
getLevels: () => levels.get(),
/**
* get levels displayed on screen.
* This includes the one with add level button
* */
getOnScreen: () => levels.numOnScreen,
/**
* get levels recorded using LEVELS
* This excludes the one with add level button
* */
getRecored: () => levels.numRecoreded,
/** add new level */
add: (newLevel: Level) => levels.add(newLevel),
/** update levels */
update: (updateLevel: Level, id: number) => {
const tmpLevel = new Levels();
id -= 1;
try {
for (let i = 0; i < levels.levels.length; i++) {
if (id == i) {
tmpLevel.add(updateLevel);
} else {
tmpLevel.add(levels.levels[i]);
}
}
levels.levels = tmpLevel.levels;
console.log(`post update:`);
LEVELS.print();
return true;
} catch (e) {
console.log(e);
return false;
}
},
print: () =>
levels.levels.forEach(level =>
console.debug(
`difficulty_factor: ${level.difficulty_factor} visitor ${level.visitor_threshold}`,
),
),
/** remove level */
remove: (id: number) => {
console.debug(`[LEVELS] received order to remove ${id} element`);
const tmpLevel = new Levels();
id -= 1;
try {
for (let i = 0; i < levels.levels.length; i++) {
if (id != i) {
tmpLevel.add(levels.levels[i]);
} else {
console.debug(`[LEVELS] removing ${i} element`);
const rmElement = levels.levels[i];
console.debug(
`[LEVELS] removing element:
difficulty_factor: ${rmElement.difficulty_factor}
visitor_threshold: ${rmElement.visitor_threshold}`,
);
}
}
levels.levels = tmpLevel.levels;
console.debug('Post remove:');
LEVELS.print();
return true;
} catch (e) {
console.log(e);
return false;
}
},
};
})();

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import CONST from '../const';
import getLevelFields from './getLevelFields';
import {LEVELS} from './index';
/** on-change event handler to update level */
const updateLevel = (e: Event) => {
const target = <HTMLInputElement>e.target;
const id = target.id;
let level;
if (id.includes(CONST.VISITOR_WITHOUT_LEVEL)) {
level = id.slice(CONST.VISITOR_WITHOUT_LEVEL.length);
} else if (id.includes(CONST.DIFFICULTY_WITHOUT_LEVEL)) {
level = id.slice(CONST.DIFFICULTY_WITHOUT_LEVEL.length);
} else {
throw new Error(
'update event was triggered by some element other than difficulty or visitor',
);
}
level = parseInt(level);
if (Number.isNaN(level)) {
console.error(`[updateLevel.ts] level # computed is not correct, got NaN`);
}
try {
const updatedLevel = getLevelFields(level);
LEVELS.update(updatedLevel, level);
} catch (e) {
alert(e);
}
};
/** registers on-change event handlers to update levels */
export const register = (id: number) => {
const visitorID = CONST.VISITOR_WITHOUT_LEVEL + id.toString();
const difficultyID = CONST.DIFFICULTY_WITHOUT_LEVEL + id.toString();
const visitorElement = <HTMLInputElement>document.getElementById(visitorID);
const difficultyElement = <HTMLInputElement>(
document.getElementById(difficultyID)
);
visitorElement.addEventListener('input', updateLevel, false);
difficultyElement.addEventListener('input', updateLevel, false);
};

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {LEVELS} from './index';
import getLevelFields from './getLevelFields';
/**
* Fetches level from DOM using the ID passesd and validates
* its contents
* */
const validateLevel = (id: number) => {
const level = getLevelFields(id);
if (level === null) {
return false;
}
try {
LEVELS.add(level);
return true;
} catch (e) {
return false;
}
};
export default validateLevel;

View File

@@ -0,0 +1,113 @@
/*
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
@import '../../../reset';
@import '../../../vars';
@import '../../../components/button';
@import '../../../components/forms';
.sitekey-form {
display: flex;
flex-direction: column;
width: 90%;
justify-content: center;
align-items: center;
box-sizing: content-box;
background-color: $white;
margin: auto;
padding-bottom: 30px;
}
.form__title-flex-container {
display: flex;
width: 100%;
border-bottom: 0.1px solid $light-grey;
}
.form__title {
padding-left: 10px;
font-size: 1rem;
padding: 0.75rem 1.25rem;
box-sizing: border-box;
text-align: left;
width: 100%;
border-bottom: 0.1px solid $light-grey;
}
.sitekey-form__label {
@include form-label;
}
.sitekey-form__input {
@include form-input;
width: 100%;
}
// level styling
.sitekey__level-container {
width: $form-content-width;
box-sizing: border-box;
display: flex;
}
.sitekey__level-title {
margin-bottom: 10px;
margin-top: 5px;
}
.sitekey-form__level-label {
@include form-label;
font-size: 0.9rem;
}
.sitekey-form__level-name {
@include form-label;
display: block;
}
.sitekey-form__level-input {
@include form-input;
flex: 2;
}
.sitekey-form__level-add-level-button {
@include violet-button;
}
.sitekey-form__level-add-level-button:hover {
@include violet-button-hover;
}
.sitekey-form__level-label--hidden {
@include form-label;
color: $white;
flex: 1;
}
// level styling ends
.sitekey-form__submit {
@include violet-button;
display: block;
margin-top: 50px;
width: $form-content-width;
width: 90%;
}
.sitekey-form__submit:hover {
@include violet-button-hover;
}

View File

@@ -0,0 +1,160 @@
/*
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {LEVELS} from './levels/index';
import getNumLevels from './levels/getNumLevels';
import CONST from './const';
const REMOVE_LEVEL_BUTTON = 'sitekey-form__level-remove-level-button';
/**
* Gets executed when 'Remove' Button is clicked to remove levels
*/
const removeLevel = (e: Event) => {
const eventTarget = <HTMLElement>e.target;
const PARENT = <HTMLElement>eventTarget.parentElement;
const FIELDSET = <HTMLElement>PARENT.parentElement;
const levelNum = parseInt(
eventTarget.id.slice(CONST.REMOVE_LEVEL_BUTTON_ID_WITHOUT_LEVEL.length),
);
if (Number.isNaN(levelNum)) {
const msg =
'[removeLevelButton.ts] error in parsing level number from remove button ID';
//console.error(msg);
throw new Error(msg);
}
updateLevelNumbersOnDOM(levelNum);
LEVELS.remove(levelNum);
FIELDSET.remove();
};
/** update level number on fieldset legends and their ids too */
const updateLevelNumbersOnDOM = (id: number) => {
const numLevels = getNumLevels();
if (id + 1 == numLevels) {
// this is the first elemet so have to remove fist element
// and downgrade the add thingy
}
// since I'm doing id+1, I have to remove id after I'm done
// with inclreasing level numbers
for (let i = id+1; i <= numLevels; i++) {
const newLevel = i-1;
const levelGroup = document.querySelector(
`#${CONST.LEVEL_FIELDSET_ID_WITHOUT_LEVEL}${i}`,
);
if (levelGroup === null) {
const msg = `[removeLevelButton.ts]:
error when trying to fetch level group field set ${i}. got null`;
//console.error(msg);
throw new Error(msg);
}
// rename legend
levelGroup.getElementsByTagName(
'legend',
)[0].innerText = `Level ${newLevel}`;
// rename labels
const labels = <NodeListOf<HTMLLabelElement>>(
levelGroup.querySelectorAll(`.${CONST.LABEL_CLASS}`)
);
//console.log(labels);
labels.forEach(label => {
//console.log(`${label.htmlFor}`);
if (label.htmlFor.includes(CONST.VISITOR_WITHOUT_LEVEL)) {
label.htmlFor = `${CONST.VISITOR_WITHOUT_LEVEL}${newLevel}`;
}
if (label.htmlFor.includes(CONST.DIFFICULTY_WITHOUT_LEVEL)) {
label.htmlFor = `${CONST.DIFFICULTY_WITHOUT_LEVEL}${newLevel}`;
}
});
// rename inputs
const inputs = <NodeListOf<HTMLInputElement>>(
levelGroup.querySelectorAll(`.${CONST.LEVEL_INPUT_CLASS}`)
);
//console.log(inputs);
inputs.forEach(input => {
if (input.id.includes(CONST.VISITOR_WITHOUT_LEVEL)) {
//console.log(`${input.id}`);
//console.log('changing visitor_threshold input');
input.id = `${CONST.VISITOR_WITHOUT_LEVEL}${newLevel}`;
}
if (input.id.includes(CONST.DIFFICULTY_WITHOUT_LEVEL)) {
//console.log('changing difficulty input');
input.id = `${CONST.DIFFICULTY_WITHOUT_LEVEL}${newLevel}`;
}
});
levelGroup.id = `${CONST.LEVEL_FIELDSET_ID_WITHOUT_LEVEL}${newLevel}`;
/* TODO
* change field set ID
* change legend inner Text
* change visitor lable for value
* change visitor input id
* change difficulty for value
* change difficulty input id
*/
}
};
/** adds onclick event listener */
export const addRemoveLevelButtonEventListener = (level: number) => {
const removeButton = <HTMLElement>(
document.querySelector(
`#${CONST.REMOVE_LEVEL_BUTTON_ID_WITHOUT_LEVEL}${level}`,
)
);
removeButton.addEventListener('click', removeLevel);
};
/** adds onclick event listener to all remove buttons */
export const addRemoveLevelButtonEventListenerAll = () => {
const removeButtons = document.querySelectorAll(`.${REMOVE_LEVEL_BUTTON}`);
removeButtons.forEach(button =>
button.addEventListener('click', removeLevel),
);
};
/**
* Generate Remove button HTML. On-click handler should be added
* seprately
*/
export const getRemoveButtonHTML = (level: number) => {
//console.debug(`[generating HTML getHtml]level: ${level}`);
const HTML = `
${CONST.REMOVE_LEVEL_LABEL_TEXT}
<input
class="sitekey-form__level-remove-level-button"
type="button"
name="${CONST.REMOVE_LEVEL_BUTTON_ID_WITHOUT_LEVEL}${level}"
id="${CONST.REMOVE_LEVEL_BUTTON_ID_WITHOUT_LEVEL}${level}"
value="x"
/>
</fieldset>
`;
return HTML;
};