|
@@ -0,0 +1,254 @@
|
|
|
|
+//ДЗ: Functional OOP
|
|
|
|
+//Password
|
|
|
|
+//LoginForm
|
|
|
|
+//LoginForm Constructor
|
|
|
|
+//Password Verify
|
|
|
|
+let wrapper = document.querySelector(".wrapper"); // Сюда буду создавать поле ввода для пароля
|
|
|
|
+
|
|
|
|
+function Password(parent, open){ // Функция конструктор
|
|
|
|
+ let container = document.createElement("div"); // Создаю контейнер для инпутов
|
|
|
|
+ let button = document.createElement("button"); // Создание кнопки
|
|
|
|
+ button.disabled = true; // Отключаю кнопку т.к. при создании формы инпут логина по умолчанию пустой
|
|
|
|
+ let openCheck = open; // Переменная с которой я буду сравнивать открытость текста в поле ввода
|
|
|
|
+
|
|
|
|
+ let buildInputs = function(type, innerText) { // Функция для создания одних и тех же инпутов с разными типами
|
|
|
|
+ let input = document.createElement("input");
|
|
|
|
+ let label = document.createElement("label");
|
|
|
|
+
|
|
|
|
+ input.type = type;
|
|
|
|
+ label.innerText = innerText;
|
|
|
|
+ label.appendChild(input);
|
|
|
|
+
|
|
|
|
+ return label;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let toggleHiddenPassword = function(type, open) { // Функция для переключения состояния инпута с паролем с видимого на невидимый и переключение чекбокса
|
|
|
|
+ inputPassword.type = type;
|
|
|
|
+ inputCheckbox.checked = open;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ let toggleHiddenCheckPassword = function(check) { // Функция для скрытия повтора пароля если Password в открытом режиме
|
|
|
|
+ if(check) {
|
|
|
|
+ container.removeChild(checkPasswordLabel);
|
|
|
|
+ } else {
|
|
|
|
+ container.insertBefore(checkPasswordLabel, container.children[2]);
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ let verifyPassword = function(additional = true) { // Проверка на наличие символов в логине и пароле, если в одном из полей пусто то кнопка disabled
|
|
|
|
+ if(inputLogin.value.length > 0 && inputPassword.value.length > 0 && additional) {
|
|
|
|
+ button.disabled = false;
|
|
|
|
+ } else {
|
|
|
|
+ button.disabled = true;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ container.appendChild(buildInputs("login", "Login")); // Собираю форму ввода
|
|
|
|
+ container.appendChild(buildInputs("password", "Password"));
|
|
|
|
+ container.appendChild(buildInputs("password", "Повторите пароль")).classList.add("check-password"); // Тут добавил класс что бы различать два инпута, type = password, друг от друга
|
|
|
|
+ container.appendChild(buildInputs("checkbox", "Показать пароль"));
|
|
|
|
+ container.appendChild(button).innerText = "Отправить";
|
|
|
|
+ parent.appendChild(container);
|
|
|
|
+
|
|
|
|
+ let inputLogin = container.querySelector("input[type='login']"); // Нахожу все инпуты и присваиваю их в переменные для дальнейшей работы с ними
|
|
|
|
+ let inputPassword = container.querySelector("input[type='password']");
|
|
|
|
+ let inputCheckPassword = container.querySelector(".check-password input");
|
|
|
|
+ let checkPasswordLabel = inputCheckPassword.parentElement;
|
|
|
|
+ let inputCheckbox = container.querySelector("input[type='checkbox']");
|
|
|
|
+
|
|
|
|
+ open == true ? toggleHiddenPassword("text", true) : toggleHiddenPassword("password", false); // Один раз запускаю функцию toggleHiddenPassword для корректной отрисовки инпутов в зависимости от переданных параметров в конструкторе
|
|
|
|
+ if(open == true) { // Скрываю inputCheckPassword если конструктор вызвали с параметром open = true, то есть когда Password в открытом режиме
|
|
|
|
+ container.removeChild(container.querySelector(".check-password"));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this.setValue = function(value) {
|
|
|
|
+ inputPassword.value = value;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ this.getValue = function() {
|
|
|
|
+ return inputPassword.value;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ this.setOpen = function(hidden) {
|
|
|
|
+ hidden == true ? toggleHiddenPassword("text", true) : toggleHiddenPassword("password", false);
|
|
|
|
+ openCheck = hidden;
|
|
|
|
+ toggleHiddenCheckPassword(hidden);
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ this.getOpen = function() {
|
|
|
|
+ return openCheck;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ container.oninput = (evt) => { // Навешиваю слушатель события на весь контейнер, а не на каждый инпут в отдельности
|
|
|
|
+ if(evt.target.type == "text" || evt.target.type == "password") { // Тут идет проверка что изменения происходят на инпутах типа text или password для передачи value в onChange
|
|
|
|
+ this.onChange(evt.target.value);
|
|
|
|
+ } else { // Если эти изменения в инпуте с другим типом (Остается только чекбокс) то я запускаю setOpen с противоположным значением openCheck, как бы переключаю значение на противоположное
|
|
|
|
+ this.setOpen(!openCheck);
|
|
|
|
+ this.onOpenChange(openCheck);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if(openCheck == true) { // Тут идет проверка, если Password в открытом режиме то мне не нужно проверять на совпадение value у inputPassword и inputCheckPassword
|
|
|
|
+ verifyPassword();
|
|
|
|
+ } else {
|
|
|
|
+ verifyPassword(inputPassword.value == inputCheckPassword.value);
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+let p = new Password(wrapper, true);
|
|
|
|
+
|
|
|
|
+p.onChange = data => console.log(data);
|
|
|
|
+p.onOpenChange = open => console.log(open);
|
|
|
|
+
|
|
|
|
+p.setValue('qwerty');
|
|
|
|
+console.log(p.getValue());
|
|
|
|
+
|
|
|
|
+p.setOpen(false);
|
|
|
|
+console.log(p.getOpen());
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+//Form
|
|
|
|
+//Отображение
|
|
|
|
+//Sync
|
|
|
|
+//Validators
|
|
|
|
+//Many Inputs
|
|
|
|
+//Validators messages
|
|
|
|
+//OkButton
|
|
|
|
+//CancelButton
|
|
|
|
+let formContainer = document.querySelector(".form-container"); // Контейнер для формы
|
|
|
|
+
|
|
|
|
+function Form(el, data, okCallback, cancelCallback){
|
|
|
|
+ let formBody = document.createElement('div')
|
|
|
|
+ let okButton = document.createElement('button')
|
|
|
|
+ let table = document.createElement("table"); // Создаю таблицу, в ней буду отрисовывать key и value из data
|
|
|
|
+ let copyData = data; // Копия объекта data для возможности перейти к дефолтным значениям
|
|
|
|
+ okButton.innerHTML = 'OK'
|
|
|
|
+
|
|
|
|
+ let cancelButton = document.createElement('button')
|
|
|
|
+ cancelButton.innerHTML = 'Cancel'
|
|
|
|
+
|
|
|
|
+ formBody.innerHTML = '<h1>тут будет форма после перервы</h1>'
|
|
|
|
+ if (typeof okCallback === 'function'){
|
|
|
|
+ formBody.appendChild(okButton);
|
|
|
|
+ okButton.onclick = (e) => {
|
|
|
|
+ console.log(this)
|
|
|
|
+ this.okCallback(this.data) // Отдаю текущий поредактированный объект data первым параметром в okCallback
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (typeof cancelCallback === 'function'){
|
|
|
|
+ formBody.appendChild(cancelButton);
|
|
|
|
+ cancelButton.onclick = cancelCallback
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let trCreator = function(key, value, trElement, labelElement, inputElement, inputType) { // Функция для корректной сборки таблицы и расположению в ней key и value
|
|
|
|
+ for(let i = 0; i < 2; i++) {
|
|
|
|
+ let td = document.createElement("td");
|
|
|
|
+
|
|
|
|
+ if(i == 0) { // В tr нужно поместить два элемента tb, в первом должен быть label с key внутри, а во втором input, собсна тут я этим и занимаюсь
|
|
|
|
+ labelElement.innerText = key;
|
|
|
|
+ labelElement.htmlFor = key;
|
|
|
|
+ td.appendChild(labelElement);
|
|
|
|
+ } else {
|
|
|
|
+ inputElement.value = value;
|
|
|
|
+ inputElement.id = key;
|
|
|
|
+ inputElement.type = inputType;
|
|
|
|
+ td.appendChild(inputElement);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ trElement.appendChild(td);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return trElement;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ let inputCreators = {
|
|
|
|
+ String(key, value, oninput){
|
|
|
|
+ let tr = document.createElement("tr");
|
|
|
|
+ let label = document.createElement("label");
|
|
|
|
+ let input = document.createElement("input");
|
|
|
|
+
|
|
|
|
+ input.oninput = () => oninput(input.value);
|
|
|
|
+
|
|
|
|
+ return trCreator(key, value, tr, label, input, "text");
|
|
|
|
+ },
|
|
|
|
+ Boolean(key, value, oninput){
|
|
|
|
+ let tr = document.createElement("tr");
|
|
|
|
+ let label = document.createElement("label");
|
|
|
|
+ let input = document.createElement("input");
|
|
|
|
+
|
|
|
|
+ if(value == true) {
|
|
|
|
+ input.checked = true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ input.oninput = () => oninput(input.checked);
|
|
|
|
+
|
|
|
|
+ return trCreator(key, value, tr, label, input, "checkbox");
|
|
|
|
+ },
|
|
|
|
+ Date(key, value, oninput){
|
|
|
|
+ let tr = document.createElement("tr");
|
|
|
|
+ let label = document.createElement("label");
|
|
|
|
+ let input = document.createElement("input");
|
|
|
|
+
|
|
|
|
+ let timespan = value.getTime();
|
|
|
|
+ let dateValue = ((new Date(timespan - value.getTimezoneOffset() * 60 * 1000)).toISOString()).slice(0, -14);
|
|
|
|
+
|
|
|
|
+ input.oninput = () => oninput(new Date(input.value));
|
|
|
|
+
|
|
|
|
+ return trCreator(key, dateValue, tr, label, input, "date");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for(let [key, value] of Object.entries(data)) { // Итерирую объект data и создаю для каждой пары ключ-значение input
|
|
|
|
+ let inputCreatorFunction = inputCreators[value.constructor.name];
|
|
|
|
+
|
|
|
|
+ let tr = inputCreatorFunction(key, value, (newValue) => {
|
|
|
|
+ if(this.validators[key] != undefined) { // Если this.validator[key] не равен undefined для этого input предусмотрена валидация и поэтому ее нужно запустить
|
|
|
|
+ if(typeof(this.validators[key](newValue, key, data, tr.querySelector(`#${key}`))) == "string") { // Если все это чудо вернуло тип данных который равен "String" то значит поле не прошло валидацию
|
|
|
|
+ let span = document.createElement("span"); // Тут будет лежать текст ошибки
|
|
|
|
+ span.innerText = this.validators[key](newValue, key, data, tr.querySelector(`#${key}`)); // Собсна пишу сам текст ошибки в span
|
|
|
|
+ span.style.position = "absolute"; // Задаю спану absolute что бы табличка не скакала когда я его добавлю в tr
|
|
|
|
+
|
|
|
|
+ tr.style.backgroundColor = "red";
|
|
|
|
+ tr.appendChild(span);
|
|
|
|
+
|
|
|
|
+ okButton.disabled = true; // Отключаю кнопку okButton если форма не прошла валидацию
|
|
|
|
+ } else {
|
|
|
|
+ tr.style.backgroundColor = "inherit";
|
|
|
|
+ data[key] = newValue;
|
|
|
|
+ if(tr.querySelector("span") != null) { // Тут идет проверка на наличие span с ошибкой, если его нет то удалять не нужно
|
|
|
|
+ tr.removeChild(tr.querySelector("span"));
|
|
|
|
+
|
|
|
|
+ okButton.disabled = false; // Включаю кнопку okButton если форма прошла валидацию
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ } else {
|
|
|
|
+ data[key] = newValue;
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ table.appendChild(tr);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ formBody.insertBefore(table, formBody.children[1]);
|
|
|
|
+ el.appendChild(formBody);
|
|
|
|
+
|
|
|
|
+ this.okCallback = okCallback
|
|
|
|
+ this.cancelCallback = cancelCallback
|
|
|
|
+
|
|
|
|
+ this.data = data
|
|
|
|
+ this.validators = {}
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+let form = new Form(formContainer, {
|
|
|
|
+ name: 'Anakin',
|
|
|
|
+ surname: 'Skywalker',
|
|
|
|
+ married: true,
|
|
|
|
+ birthday: new Date((new Date).getTime() - 86400000 * 30*365)
|
|
|
|
+}, () => console.log('ok'),() => console.log('cancel') )
|
|
|
|
+form.okCallback = (e) => console.log(e);
|
|
|
|
+
|
|
|
|
+form.validators.surname = (value, key, data, input) => value.length > 2 &&
|
|
|
|
+ value[0].toUpperCase() == value[0] &&
|
|
|
|
+ !value.includes(' ') ? true : 'Wrong name'
|
|
|
|
+console.log(form)
|