diff --git a/about.txt b/about.txt index 65cd6f9..dc621d6 100644 --- a/about.txt +++ b/about.txt @@ -1,5 +1,3 @@ -Made By Daniel Enesi, do not copy, do not pirate. +Made By Daniel Enesi. Do not misuse. -enesidaniel.120064@gmail.com, +2347010081471 - -Asks interview questions and records answers, either in text or in voice +Asks interview questions and records answers, either in text or in video. diff --git a/classes.js b/classes.js new file mode 100644 index 0000000..5114237 --- /dev/null +++ b/classes.js @@ -0,0 +1,266 @@ +class Question{ + constructor(question="What do you want to be asked?"){ + this._question = question; + this.build(); + this.event(); + Question.questions.push(this); + } + get question(){ + return this.text.value; + } + set question(ask){ + this.text.value = ask; + } + build(){ + add( + (this.text = make("input")) + , add(this.box = make("li"), QUESTIONLIST) + ).value = this._question; + this.text.type = "text"; + + ["mover", "deleter"].forEach((type, i)=>{ + add( + (this[type] = make("button")), this.box + ).className = "icon"; + this[type].textContent = Question.iconValues[i]; + }); + ["box", "text", "mover", "deleter"].forEach(dom=>this[dom].obj=this); + this.mover.draggable = true; + } + event(){ + this.deleter.onclick=()=>this.delete(); + this.text.onkeyup=(event)=>{ + event.key=="Enter" && event.ctrlKey && (new Question).text.focus(); + } + this.mover.onkeydown=(event)=>{ + Question.keys.includes(event.key) && event.preventDefault(); + } + this.mover.onkeyup=(event)=>{ + if (Question.keys.includes(event.key) && + this.box[`${Question.keyMap[event.key]}ElementSibling`]){ + event.preventDefault(); + if (event.key.includes("Up")){ + QUESTIONLIST.insertBefore(this.box, this.box.previousElementSibling); + } else{ + QUESTIONLIST.insertBefore(this.box.nextElementSibling, this.box); + } + this.mover.focus(); + } + } + this.mover.ondragstart=(event)=>{ + this.box.classList.add("highlight"); + } + this.mover.ondragend=(event)=>{ + let at = document.elementFromPoint(event.x, event.y); + if (at && at.obj && at.obj != this){ + QUESTIONLIST.insertBefore(this.box, at.obj.box) + } + this.box.classList.remove("highlight"); + } + // this.mover.onmouse + this.text.onblur=()=>{ + if (!this.question.trim()){ + this.question = this._question; + } + } + } + delete(){ + this.box.remove(); + remove(this, Question.questions); + Question.deletedQuestions.push(this); + } + restore(){ + QUESTIONLIST.append(this.box); + } + static restoreLast(){ + SETUP.checkVisibility() && Question.deletedQuestions.length && Question.deletedQuestions.pop().restore(); + } + static questions = []; + static iconValues = ['<>', 'X']; + static deletedQuestions = []; + static keyMap = {"ArrowUp": "previous", "ArrowDown": "next"}; + static keys = Object.keys(Question.keyMap); +} + +class Switch{ + constructor(where, obj, property, values){ + [this.container, this.obj, this.property, this.values] = arguments; + this.build(); + this.event(); + Switch.swtiches.push(this); + } + build(){ + add((this.switch = make()) + , add((this.holder = make()), this.container) + ).className = "switch"; + this.holder.className = "switchold"; + // this.switch.tabIndex = -1; + } + event(){ + this.switch.onclick = (event)=>{ + event.preventDefault(); + this.holder.classList.contains("off") + ?this.holder.classList.remove("off") + :this.holder.classList.add("off"); + this.action(); + } + } + get on(){ + return !this.holder.classList.contains("off") + } + action(){ + // all switches will set a property of something + // between to two values (flex/none) (true/false) + // 1/0 + let [obj, property, values] = [this.obj, this.property, this.values]; + obj[property] = obj[property] == values[0]?values[1]:values[0] + } + static swtiches = []; +} + +class Help{ + constructor(){ + this.messages = [ + "Use ctrl+z to restore last delted question" + , "Hit enter on a question box to add another question" + , "etc" + ] + } +} + +class SearchUI{ + // to specific + constructor(where, values){ + [this.container, this._values] = arguments; + this.build(); + this.event(); + } + get values(){ + if (typeof this._values == "function"){ + return this._values(); + } else{ + return this._values; + } + } + build(){ + add((this.searchBox = make("input")) + , add((this.box = make()) + , this.container)).type = "text"; + this.box.className = "searchui"; + add((this.list = make("ul")), this.box).hidden = true; + this.list.size = 20 + } + event(){ + // change onvoice... to event variable/not + speechSynthesis.onvoiceschanged=(event)=>{ + let voices = this.values; + for (let i in voices){ + add(make("li"), this.list).textContent = voices[i]; + } + } + this.searchBox.onfocus=()=>this.list.hidden=false; + this.searchBox.addEventListener("blur" + , ()=>setTimeout(()=>this.list.hidden=true, 500)); + this.searchBox.oninput=(event)=>{ + [...this.list.children].forEach(child=>{ + child.hidden = !(child.textContent.toLowerCase().includes(this.searchBox.value.toLowerCase())); + }); + if (this.value){ + TALK.voice = getVoice(this.value); + } + } + this.list.onclick=(event)=>{ + event.stopImmediatePropagation(); + event.stopPropagation(); + if (event.target.parentElement == this.list){ + this.value = event.target.textContent; + } + } + } + get value(){ + if (this.values.includes(this.searchBox.value)){ + return this.searchBox.value; + }; + return false; + } + set value(name){ + this.searchBox.value = name; + TALK.voice = getVoice(name); + } +} + +// modals +class Modal{ + constructor(buttons){ + this.buttons = buttons; + this.message = ""; + this.build(); + this.event(); + Modal.modals.push(this); + } + build(){ + add(this.modal = make()).className = "modal"; + add(this.messageBox = make('p'), this.modal); + this.buttons.forEach((button, pos)=>{ + add(this[button] = make("button"), this.modal).textContent = button; + this[button].pos = pos; + }) + } + event(){ + this.modal.onclick=(event)=>{ + if (event.target.nodeName == "BUTTON"){ + this.method(event.target.pos); + this.close(); + } + } + } + changeButtons(buttons){ + this.buttons.forEach((button, pos)=>{ + this[button].textContent = buttons[pos]; + this[button].pos = pos; + this[buttons[pos]] = this[button]; + }) + this.buttons = buttons; + } + open(message, buttons){ + // do not do anything if a modal is already up + if (Modal.showing) { + this.blink(); + return; + }; + showLoading() + if (buttons) this.changeButtons(buttons); + this.messageBox.innerHTML = message; + this.modal.classList.add("shown"); + this[this.buttons[this.buttons.length-1]].focus(); + // promise + // will be given to the event onclick + return new Promise((resolve)=>this.method = resolve); + } + close(){ + this.modal.classList.remove("shown", "blink"); + hideLoading(); + } + blink(){ + this.modal.classList.add("blink"); + setTimeout(()=>this.modal.classList.remove("blink"), 500); + } + get shown(){ + return this.modal.classList.contains("shown"); + } + static get showing(){ + return Modal.modals.some(modal=>modal.shown); + } + static modals = []; + static alertBox = new Modal(["CLOSE"]); + static confirmBox = new Modal(["CANCEL", "OK"]); +} +function alert(message, buttons){ + return Modal.alertBox.open(message, buttons); +} +function confirm(message, buttons){ + return Modal.confirmBox.open(message, buttons); +} + +// maise: young girl +// make search better by giving properties \ No newline at end of file diff --git a/events.js b/events.js index 0914301..29d0af2 100644 --- a/events.js +++ b/events.js @@ -1,118 +1,61 @@ -//all functions and variables if (except all event based) here -onbeforeunload=()=>{ - speechSynthesis.cancel(); - localStorage.questions = JSON.stringify([...questionslist.children].map(i=>i.querySelector('.question').innerText)); -} -addquestion.onclick=()=>{ - makeQuestion('Write a question here, write a question here', questionslist.childElementCount+1) -} -startbutton.onclick=()=>{ - if (!(questions=[...questionslist.children].map(i=>i.querySelector('.question').innerText)).length) return errors.innerHTML = 'No question added, Please add at least one questions to proceed'; - if (!confirm('Proceed?')) return - embassy.style.display='flex'; setup.style.display='none'; - shouldusevideo.checked?you.style.display='none':interviewbox.style.display='none'; - questionbox.innerText=(currentQuestion=questions[index])//setup non-video - if (!shouldusevideo.checked) return; - // while (!voice){} - loading.loading = 1; - userMedia = navigator.mediaDevices.getUserMedia({ - audio: true, video: true, - facingMode: {exact: "user"} - }); - // use transform scale(-1) on a container containing the video. - userMedia.then(mediaStream=>{ - //setup video recording - stream = mediaStream; - recorder = new MediaRecorder(mediaStream) - recorder.ondataavailable=()=>{ - loading.loading = 1; - finalFile = new Blob([event.data], {type:"video/mp4"}) - response.src=URL.createObjectURL(finalFile); - refresher.style.display='block'; - responseHolder.style.display='flex'; - loading.loading = 0; - } - recorder.start(); - video.srcObject = mediaStream; video.muted=true; - }).catch(error=>{alert('an error occurred, exitting!!'); location.reload()}) - video.onloadstart=()=>{ - loading.loading=0; - video.play(); - let hours = new Date(Date.now()).getHours(), time; - if(hours>16){ - time ='evening' - }else if(hours>11){ - time='afternoon' - }else if(hours>=0){ - time='morning' - } - question = new SpeechSynthesisUtterance(` - Good ${time}}. Welcome to your interview. - Please click the repeat button to repeat any asked question, - and click the forward button to go to the next question.`); - question.voice =voice//= voices[+getAll('[name=voice]').filter(v=>v.checked)[0].value] - voice; question.rate=.8; - speechSynthesis.speak(question); - question.onstart=()=>say.disabled=forward.disabled=true; - question.onend = ()=>{setTimeout(()=>!video.paused&&(say.disabled=forward.disabled=false), randBtw(1000, 2500))}; - // setup q and a - } - -} -answerquestion.onclick=()=>{ - event.preventDefault(); - if (!answerbox.reportValidity()) return; - answers.push(answerbox.value); answerbox.value=''; - if(index+1>=questions.length){ - //submit - embassy.style.display='none' - responses.style.display='flex'; - questions.forEach((q, i)=>{ - let r = responses.insertRow(); - (r.insertCell()).innerText = q; - (r.insertCell()).innerText = answers[i] - }) - return refresher.style.display='block' +// //all functions and variables if (except all event based) here +onload=()=>{ + // Turn all elements with ID into variables + identify(); + // make LOADING DIV + loader(); + // show only SETUP + switchScreen("SETUP"); + // restore saved questions + restoreSavedData(); + // consent to recording + consentToRecording(); + + // TESTS + // a = alert("Hi!").then(console.log); + // videoSwitch.switch.click() +} +// add event listeners +addEventListener("keyup", (event)=>{ + if (event.key == 'b' && event.ctrlKey){ + Question.restoreLast(); } - questionbox.innerText=(currentQuestion = questions[++index]) - if(index+2>questions.length) answerquestion.innerText='End interview'; +}) +// add question button +ADDQUESTION.onclick=()=>new Question + +// restore question button +RESTOREQUESTION.onclick=()=>Question.restoreLast(); + +// TESTing voices +TESTVOICE.onclick =()=>{ + speechSynthesis.cancel(); + if (!speechSearch.value) return; + say(`Hi, this is speech synthesis, using ${TALK.voice.name}`) } -playorpause.onclick=()=>{ - if (video.paused) { - speechSynthesis.resume(); recorder.resume(); video.play(); playorpause.innerText = 'Pause Recording'; - say.disabled=forward.disabled=false; - } else { - speechSynthesis.pause(); recorder.pause(); video.pause();playorpause.innerText = 'Continue Recording'; - question.onstart(); + +onbeforeunload=()=>{ + speechSynthesis.cancel(); + saveData();} + +STARTBUTTON.onclick=()=>{ + saveData(); + questions = JSON.parse(localStorage.questions); + if (!questions.length){ + return alert("You have to add at least one question"); + } else if (videoSwitch.on && !speechSearch.value) { + return alert("Please, choose a voice!"); } -} -forward.onclick=()=>{ - question.onstart(); - if(index>=questions.length){ - //submit - question.text = after.value?after.value:after.placeholder; - speechSynthesis.speak(question) - question.onend=()=>{ - embassy.style.display='none' - stream.getTracks().forEach(i=>i.stop()); - recorder.stop(); + confirm("Are you sure you want to begin?", ["No", "Yes"]) + .then(resp=>{ + if (resp){ + ( + interview = new Interview( + JSON.parse(localStorage.questions) + , videoSwitch.on + )).start(); + } else{ + alert("Make changes then click START"); } - return - } - question.text=(currentQuestion=questions[index++]); - speechSynthesis.speak(question) - if(index+1>questions.length) forward.innerText='End interview'; -} -say.onclick=()=>{ - speechSynthesis.cancel(); - speechSynthesis.speak(question); -} -// addquestion.click() -//for downloading -save.onclick=()=>{ - downloader.href=response.src; - downloader.click(); -} -getAll('[name=voice]').forEach(v=>{ - v.onchange=()=>{voice=speechSynthesis.getVoices()[+v.value]} -}) + }) +} \ No newline at end of file diff --git a/funcs.js b/funcs.js index 1469a46..7e3e940 100644 --- a/funcs.js +++ b/funcs.js @@ -1,16 +1,28 @@ -//make, get, identify, getall, randbtw +// dom functions let make = (name='div')=>document.createElement(name) , get = (id)=>document.getElementById(id) , getE = (selector,value)=>document.querySelector(`[${selector}=${value}]`) , getS = (query)=>document.querySelector(query) - , getAll = (query)=>[...document.querySelectorAll(query)]; -let identify = ()=>getAll('[id]').forEach(i=>window[i.id] = i) + , getAll = (query)=>[...document.querySelectorAll(query)] + , identify = ()=>getAll('[id]').forEach(i=>window[i.id] = i) , add = (what,to=document.body)=>to.appendChild(what) - , bx = (who)=>who.getBoundingClientRect(); + , bx = (who)=>who.getBoundingClientRect() + , show = (what) => what.style.display="" + , hide = (what) => what.style.display="none" + , remove = (what, from) => from.splice(from.indexOf(what), 1); -function randBtw(x=0, y=0, prec=0) { - let n = `${(y - x + 1) * Math.random() + x}`; - let s = n.split('.') - , N = s[0] + s[1].slice(0, prec) - return Number(N) + +// switch screen (still DOM) +function switchScreen(screenID){ + getAll("body>div").forEach(div=>{if (div.id) div.style.display = "none"}); + get(screenID).style.display = ""; +} + +// math +// not inclusive of the last one (a is the little one) +function randBtw(a, b){ + return a+parseInt((b-a)*Math.random()); } + +// choice +choice = (array)=>array[randBtw(0, array.length)] \ No newline at end of file diff --git a/index.html b/index.html index 989b9b5..8bba9fa 100644 --- a/index.html +++ b/index.html @@ -1,69 +1,104 @@ - - + + +
+ + +Stay focused. You can do this!
++ If you had two people, one of them who knows your name. + How will you tell your name to the other person? +
+ +Questions | +Answers | +
---|
Your Responses | -|
Questions | -Answers | -
---|