diff --git a/DOCS.md b/DOCS.md index bf5156a..6a1fbc6 100644 --- a/DOCS.md +++ b/DOCS.md @@ -146,6 +146,8 @@ map.tag(/* TAG элемента */); // Вернет экземпляр map дл map.attach(/* событие */, /* функция */); // EventHandler map.attribute(/* Имя */, /* Значение */); // Задание атрибута map.add(/* HTML */); +map.all(/* CSS-селектор */); // Вернет массив экземпляров map для выбранных элементов +map.property(/* свойство элемента */[, /* значение */]); /* * Функция в attach может принимать параметр. * Этот параметр - обьект Find, map которого был вызван diff --git a/Find/Find.js b/Find/Find.js index 40d5b2e..fd37302 100644 --- a/Find/Find.js +++ b/Find/Find.js @@ -31,6 +31,26 @@ class FindObjectMap { this._num = num; return this; } + all(q) { + const o = this.root.querySelectorAll(q); + if (typeof o === "undefined") { + PrivateFind.error("Нет обьектов для селектора!"); + return undefined; + } + let arr = new Array(); + for (let i = 0; i < o.length; i ++) { + arr[i] = new FindObjectMap(o[i], this._app, this._num + 1) + } + return arr; + } + property(property, to) { + if (typeof to === "undefined") { + return this.root[property]; + } else { + this.root[property] = to; + return this; + } + } select(q) { const o = this.root.querySelector(q); if (typeof o === "undefined") { @@ -39,6 +59,12 @@ class FindObjectMap { } return new FindObjectMap(o, this._app, this._num + 1); } + status() { + if (typeof this.root === "object" && this.root !== null && this.root !== undefined) { + return true; + } + return false; + } read() { return this.root.innerHTML; } @@ -85,7 +111,7 @@ class FindObjectMap { } attach(event, func) { this.root.addEventListener(event, () => { - func(this._app); + func(this._app, this); }); return this; } diff --git a/demos/Quiz/image.avif b/demos/Quiz/image.avif new file mode 100644 index 0000000..af174b2 Binary files /dev/null and b/demos/Quiz/image.avif differ diff --git a/demos/Quiz/index.js b/demos/Quiz/index.js index f23a73a..b7118de 100644 --- a/demos/Quiz/index.js +++ b/demos/Quiz/index.js @@ -1,20 +1,33 @@ -const qestions = [ +const questions = [ { - qestion: "Мой вопрос", + image: "image.avif", + question: "Мой вопрос", variants: [ "Вариант 1", "Вариант 2", "Вариант 3" ], correct: 0 + }, + { + question: "Мой вопрос", + variants: [ + "Вариант 1", + "Вариант 2", + "Вариант 3" + ], + correct: [0, 1] } ]; class Quiz extends Find.createApp() { - constructor(qestions) { + constructor(questions) { const root = Find.search('root'); super(root); - this.qestions = qestions; + this.question_type = undefined; + this.questions = questions; + this.question = null; + this.correctCount = 0; } init() { @@ -22,11 +35,12 @@ class Quiz extends Find.createApp() { this.setMeta(); this.setCard(); this.stable(); - this.createQestions(); + this.createQuestions(); } setCard() { this.add([ + Find.content('
'), Find.content( `
@@ -41,7 +55,7 @@ class Quiz extends Find.createApp() {
` @@ -52,63 +66,274 @@ class Quiz extends Find.createApp() { setMeta() { this.meta([ Find.content(''), - Find.content('') + Find.content(''), + Find.content( + `` + ) ]); } - createQestions() { + createQuestions() { this.number = 0; - for (let i = 0; i < this.qestions.length; i ++) { - this.select('qestion' + this.number); - this.setTitle(); - this.render(); - this.setForms(this.qestions[i]); - this.setEvents(this.qestions[i]) - .then((result) => { + this.questionsWaiter(null); + } + + questionsWaiter(status) { + const results = [ + { + source: "no_select", + text: "Не выбран не один вариант!", + type: "error" + }, + { + source: "incorrect", + text: "Неправильный ответ!", + type: "error" + }, + { + source: "correct", + text: "Правильный ответ!", + type: "success" + } + ]; + + const isEnd = (this.questions.length === this.number); + if (!isEnd) this.question = this.questions[this.number]; + this.select('question' + this.number); + this.setTitle(); + this.render(); + this.setStatus(status); + this.setForms(isEnd); + this.attachDeleteButton(); + if (isEnd) return; + this.setEvents().then((result) => { + if (result !== 'no_select') { this.number ++; - }) - .catch((error) => { - - }); - this.number ++; + } + + let obj = undefined; + for (obj of results) { + if (obj.source == result) break; + } + + this.questionsWaiter(obj); + }); + } + + setStatus(status) { + const root = this.component(); + const message = root.id('message'); + if (status !== null) { + const elem = status.type === 'error' ? + 'is-danger' : + 'is-success'; + message.html( + Find.content( + `
+ + ${status.text} +
` + ) + ); } } - setEvents() {} - - setTitle() { - this.title('Quiz qestion ' + this.number + ' - Find.js demo'); + attachDeleteButton() { + const root = this.component(); + const button = root.id('delete-info'); + if (button.status()) button.attach('click', app => { + const root = app.component(); + root.id("message").html(''); + }); } - setForms(qe) { + setEvents() { + const root = this.component(); + const que = this.question; + const main = this; + return new Promise((resolve, reject) => { + const get_select = function() { + const button = root.id('test_select'); + button.attach('click', () => { + const name = 'question'; + const type = main.question_type; + let correct = false; + + if (type === 'radio') { + const elem = root.select(`input[name="${name}"]:checked`); + + if (!elem.status()) { + resolve('no_select'); + return; + } + + const answer = Number(elem.value()); + + if (answer === que.correct) correct = true; + } else { + const elements = root.all('input[type="checkbox"]:checked'); + + if (elements.length === 0) { + resolve('no_select'); + return; + } + + const answer = elements.map(elem => Number(elem.property('id').slice(7))); + + let correct_count = 0; + for (let i = 0; i < que.correct.length; i ++) { + + if ( + answer[i] === undefined || + answer[i] === null + ) break; + + if (answer[i] === que.correct[i]) { + correct_count ++; + } + } + + if (correct_count === que.correct.length) { + correct = true; + } + } + + if (correct) { + main.correctCount ++; + resolve('correct'); + } else { + resolve('incorrect'); + } + }); + }; + try { + get_select(); + } catch (err) { + reject(err); + } + }); + } + + setTitle() { + const isEnd = (this.questions.length === this.number); + if (!isEnd) { + this.title(`Quiz вопрос ${String(this.number + 1)}/${this.questions.length} - Find.js демо`); + } else { + this.title('Результаты!'); + } + } + + setForms(isDone) { + if (isDone) { + this.viewResults(); + return; + } + + const que = this.question; let input_id = 0; const root = this.component(); const content = root.id("content"); const title = root.id("title"); - title.text(qe.qestion); - for (let val in (qe.variants)) { - content.add( - Find.content( - ` - - -
` - ) + title.text(que.question); + + if (Array.isArray(que.correct)) { + this.question_type = 'checkbox'; + } else { + this.question_type = 'radio'; + } + + let image = ''; + if (typeof que.image !== "undefined") { + image = Find.content( + `
+ Изображение к вопросу + +
` ); - content.id('text' + input_id).text(qe.variants[val]); + content.add(image); + } + + for (let val in (que.variants)) { + if (Array.isArray(que.correct)) { + this.question_type = 'checkbox'; + content.add( + Find.content( + ` +
` + ) + ); + } else { + content.add( + Find.content( + ` + + +
` + ) + ); + } + content.id('text' + input_id).text(que.variants[val]); input_id ++; } } - setEvents(r) { + viewResults() { + const root = this.component(); + const button = root.id("test_select"); + const content = root.id("content"); + const title = root.id("title"); + let text = 'Попробовать снова'; + let out = ` - ${this.correctCount / (this.questions.length / 100)}%`; + const user = + this.correctCount !== this.questions.length ? + `${this.correctCount} вопросов из ${this.questions.length}.` : + 'все вопросы!'; + if (this.correctCount > (this.questions.length / 2)) { + text = 'На главную'; + } + title.text(`Ваш результат ${out}`); + button.text(text); + content.html( + Find.content( + ` + Вы правильно ответили на ${user} + ` + ) + ); + button.attach('click', () => location.reload()); } } -const quiz = new Quiz(qestions); +const quiz = new Quiz(questions); quiz.init(); \ No newline at end of file diff --git a/demos/Quiz/nomodule-find.min.js b/demos/Quiz/nomodule-find.min.js index 972aad7..e9c75bf 100644 --- a/demos/Quiz/nomodule-find.min.js +++ b/demos/Quiz/nomodule-find.min.js @@ -1 +1 @@ -class FindObjectMap{constructor(t,r,e){return this.root=t,"object"!=typeof this.root&&PrivateFind.error("Неизвестный root!"),this._app=r,this._num=e,this}select(t){const r=this.root.querySelector(t);if(void 0!==r)return new FindObjectMap(r,this._app,this._num+1);PrivateFind.error("Нет обьектов для селектора!")}read(){return this.root.innerHTML}text(t){return 0==arguments.length?this.root.textContent:(this.root.textContent=t,this)}html(t){return 0==arguments.length?this.root.innerHTML:(this.root.innerHTML=t,this)}add(t){const r=this.html();return this.html(r+String(t))}value(t){return 0==arguments.length?this.root.value:(this.root.value=t,this)}id(t){const r=this.root.querySelector("#"+t);if(void 0!==r)return new FindObjectMap(r,this._app,this._num+1);PrivateFind.error("No objects of selector")}tag(t){const r=this.root.getElementsByTagName(t);if(void 0!==r)return new FindObjectMap(r,this._app,this._num+1);PrivateFind.error("No objects of selector")}attach(t,r){return this.root.addEventListener(t,()=>{r(this._app)}),this}merge(){if(0!=this._num)return PrivateFind.error("Вы можете создавать merge-обьект только из корневого map(), но уровень вложенности данного map() равен "+String(this._num)+"!"),this;const t=this._app.getNumberOfGroupFromId(this._app.__id);return this._app.groups[t].html=new Array(this.read()),this}app(){return this._app}main(){return new FindObjectMap(this._app.root,this._app,0)}attribute(t,r){return this.root.setAttribute(t,r),this}}class Find{constructor(t){return this._lll="",this.root=t,this.iter=0,this.groups=new Array,this.groups_count=0,this._map=new FindObjectMap(this.root,this,0),this}static createApp(){return Find}static attach(t,r){return`${t}="${r.replace('"',""")}"`}static css(t){return``}static js(t){return`