From 269e7df4b39ae74f94853de037e2b20fc5fdc245 Mon Sep 17 00:00:00 2001 From: David Almeida Date: Mon, 30 Oct 2017 15:21:34 +0100 Subject: [PATCH 01/26] Arrange global vars definition --- src/index.js | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/index.js b/src/index.js index 9258781..1a26aed 100644 --- a/src/index.js +++ b/src/index.js @@ -11,22 +11,14 @@ options = { $.ajax({ url: options.url }).done(function(data) { - questions = data.questions + var {questions} = data + var quizData // Load data from past reponses try { - quizData = JSON.parse(localStorage.getItem('quiz')) - responses = quizData.responses || [] - currentQuestion = quizData.currentQuestion || -1 - responseCount = quizData.responseCount || -1 + quizData = JSON.parse(localStorage.getItem('quiz')) || {} } catch (e) {} - - if (quizData == null) { - quizData = { - responses: [] - } - responses = quizData.responses - } + var {responses=[], currentQuestion=0, responseCount=0} = quizData // Append the progress bar to DOM From ff1b0f3ce3654cad96e5f3b09da38c9c1bf43cb8 Mon Sep 17 00:00:00 2001 From: David Almeida Date: Mon, 30 Oct 2017 17:16:41 +0100 Subject: [PATCH 02/26] refactor with operators --- src/index.js | 126 ++++++++++++++++++++++----------------------------- 1 file changed, 53 insertions(+), 73 deletions(-) diff --git a/src/index.js b/src/index.js index 1a26aed..23777fe 100644 --- a/src/index.js +++ b/src/index.js @@ -11,14 +11,14 @@ options = { $.ajax({ url: options.url }).done(function(data) { - var {questions} = data - var quizData + let {questions} = data + let quizData // Load data from past reponses try { quizData = JSON.parse(localStorage.getItem('quiz')) || {} } catch (e) {} - var {responses=[], currentQuestion=0, responseCount=0} = quizData + let {responses=[], currentQuestion=0, responseCount=0} = quizData // Append the progress bar to DOM @@ -33,56 +33,41 @@ $.ajax({ .append('
') // For each question of the json, - for (var i = 0; i < data.questions.length; i++) { - question = data.questions[i] + for (let i = 0; i < data.questions.length; i++) { + data.questions[i].input = data.questions[i].input || { type:'input' } + let {problem, input, input:{type, options}} = data.questions[i] + let inputHtml - if (question.input === undefined) { - question.input = { - type: 'input' - } - } // Construct the input depending on question type - switch (question.input.type) { + switch (type) { // Multiple options case 'checkbox': case 'radio': - var input = '
' - for (j = 0; j < question.input.options.length; j++) { - var option = question.input.options[j] - var type = question.input.type - - if (!!responses[i] && responses[i].indexOf(option.label) !== -1) { - var checked = 'checked' - } else { - var checked = '' - } - - input += '
' + + inputHtml = '
' + for (j = 0; j < options.length; j++) { + const {[j]:option} = options + const checked = !!responses[i] && responses[i].includes(option.label) ? 'checked' : '' + + inputHtml += '
' + '
' + '' + '' + '
' + '
' } - input += '
' + inputHtml += '
' break // Set of inputs (composed response) case 'inputs': - var input = '' - for (j = 0; j < question.input.options.length; j++) { - var option = question.input.options[j] - var type = 'checkbox' - - if (!!responses[i]) { - var value = responses[i][j] - } else { - var value = '' - } - - input += '' + + inputHtml = '
' + for (j = 0; j < options.length; j++) { + const {[j]:option} = options + const value = responses[i] && responses[i][j] || '' + + inputHtml += '' + '' + '' + '' + '' } - input += '
' + @@ -91,27 +76,23 @@ $.ajax({ '
 
' + inputHtml += '' break // Default: simple input default: - if (!!responses[i]) { - var value = responses[i] - } else { - var value = '' - } - var input = '
' + + const value = responses[i] || '' + inputHtml = '
' + '' + '
' } $question = $('
' + '
' + - '
' + question.problem + '
' + + '
' + problem + '
' + '
' + '
' + - input + + inputHtml + '
' + '
' ).css('display', 'none') @@ -150,47 +131,46 @@ $.ajax({ // Actions on every response submission $('#submit-response').on('click', function() { - var $inputs = $('[name^=question_' + currentQuestion + ']') - var question = questions[currentQuestion] + const $inputs = $('[name^=question_' + currentQuestion + ']') + const question = questions[currentQuestion] + let response = responses[currentQuestion] // Behavior for each question type to add response to array of responses switch (question.input.type) { case 'checkbox': case 'radio': - responses[currentQuestion] = [] + response = [] $('[name=' + $inputs.attr('name') + ']:checked').each(function(i, input) { - responses[currentQuestion].push(input.value) + response.push(input.value) }) - if (responses[currentQuestion].length === 0) { - responses[currentQuestion] = null - } + response = response.length ? response : null break case 'inputs': - responses[currentQuestion] = [] + response = [] $inputs.each(function(i, input) { - responses[currentQuestion].push(input.value) + response.push(input.value) }) break default: - responses[currentQuestion] = $inputs.val() + response = $inputs.val() } + + // Set the current responses counter - var responseCount = 0 - for (i = 0; i < responses.length; i++) { - question = questions[i] + responses[currentQuestion] = response + let responseCount = 0 + for (let i = 0; i < responses.length; i++) { + let question = questions[i] + let response = responses[i] switch (question.input.type) { case 'checkbox': case 'radio': case 'inputs': - if (!!responses[i] && !!responses[i].join('')) { - responseCount++ - } + responseCount += !!response && !!response.join('') break default: - if (!!responses[i]) { - responseCount++ - } + responseCount += !!response } } @@ -198,14 +178,13 @@ $.ajax({ $('#progress') .css('width', (responseCount / questions.length * 100) + '%') + + // Check if question had a valid answer - isQuestionAnswered = true - if (!responses[currentQuestion]) { - isQuestionAnswered = false - } - if (!!responses[currentQuestion] && !!responses[currentQuestion].length) { - for (j = 0; j < responses[currentQuestion].length; j++) { - if (!responses[currentQuestion][j]) { + let isQuestionAnswered = !!response + if (response && response.length) { + for (j = 0; j < response.length; j++) { + if (!response[j]) { isQuestionAnswered = false } } @@ -219,10 +198,10 @@ $.ajax({ // Display next question $('#quiz-form') .find('#question-' + currentQuestion).css('display', 'none') - currentQuestion = currentQuestion + 1 + $('#quiz-form') - .find('#question-' + currentQuestion).css('display', 'block') + .find('#question-' + ++currentQuestion).css('display', 'block') // If it was the las question, display final message if (responseCount === questions.length) { @@ -232,6 +211,7 @@ $.ajax({ } } + // Save current state of the quiz quizData.responses = responses quizData.responseCount = responseCount From 3518e3301686db591924b82290035a9bb6fddd06 Mon Sep 17 00:00:00 2001 From: David Almeida Date: Mon, 30 Oct 2017 17:19:48 +0100 Subject: [PATCH 03/26] Create a closure --- src/index.js | 376 +++++++++++++++++++++++++-------------------------- 1 file changed, 186 insertions(+), 190 deletions(-) diff --git a/src/index.js b/src/index.js index 23777fe..98004c4 100644 --- a/src/index.js +++ b/src/index.js @@ -1,221 +1,217 @@ -var responseCount, currentQuestion, options, -questions, responses, quizData, question, j, -$question, $resetButton, isQuestionAnswered - -responseCount = 0 -currentQuestion = 0 -options = { - url: 'data/quiz.json?' + Date.now() -} - -$.ajax({ - url: options.url -}).done(function(data) { - let {questions} = data - let quizData - - // Load data from past reponses - try { - quizData = JSON.parse(localStorage.getItem('quiz')) || {} - } catch (e) {} - let {responses=[], currentQuestion=0, responseCount=0} = quizData - - - // Append the progress bar to DOM - $('body') - .append('
' + - '
 
' + - '
') - - // Append title and form to quiz - $('#quiz') - .append('

' + data.title + '

') - .append('
') - - // For each question of the json, - for (let i = 0; i < data.questions.length; i++) { - data.questions[i].input = data.questions[i].input || { type:'input' } - let {problem, input, input:{type, options}} = data.questions[i] - let inputHtml - - - // Construct the input depending on question type - switch (type) { - - // Multiple options - case 'checkbox': - case 'radio': - inputHtml = '
' - for (j = 0; j < options.length; j++) { - const {[j]:option} = options - const checked = !!responses[i] && responses[i].includes(option.label) ? 'checked' : '' - - inputHtml += '
' + - '
' + - '' + - '' + - '
' + +(function($, JSON, localStorage){ + const options = { + url: 'data/quiz.json?' + Date.now() + } + + $.ajax({ + url: options.url + }).done(function(data) { + let {questions} = data + let quizData + + // Load data from past reponses + try { + quizData = JSON.parse(localStorage.getItem('quiz')) || {} + } catch (e) {} + let {responses=[], currentQuestion=0, responseCount=0} = quizData + + + // Append the progress bar to DOM + $('body') + .append('
' + + '
 
' + + '
') + + // Append title and form to quiz + $('#quiz') + .append('

' + data.title + '

') + .append('
') + + // For each question of the json, + for (let i = 0; i < questions.length; i++) { + questions[i].input = questions[i].input || { type:'input' } + let {problem, input, input: {type, options}} = questions[i] + let inputHtml + + + // Construct the input depending on question type + switch (type) { + + // Multiple options + case 'checkbox': + case 'radio': + inputHtml = '
' + for (j = 0; j < options.length; j++) { + const {[j]:option} = options + const checked = !!responses[i] && responses[i].includes(option.label) ? 'checked' : '' + + inputHtml += '
' + + '
' + + '' + + '' + + '
' + + '
' + } + inputHtml += '
' + break + + // Set of inputs (composed response) + case 'inputs': + inputHtml = '' + for (j = 0; j < options.length; j++) { + const {[j]:option} = options + const value = responses[i] && responses[i][j] || '' + + inputHtml += '' + + '' + + '' + + '' + + '' + + '' + } + inputHtml += '
' + + '' + + '
 
' + break + + // Default: simple input + default: + const value = responses[i] || '' + inputHtml = '
' + + '' + '
' - } - inputHtml += '
' - break - - // Set of inputs (composed response) - case 'inputs': - inputHtml = '' - for (j = 0; j < options.length; j++) { - const {[j]:option} = options - const value = responses[i] && responses[i][j] || '' - - inputHtml += '' + - '' + - '' + - '' + - '' + - '' - } - inputHtml += '
' + - '' + - '
 
' - break - - // Default: simple input - default: - const value = responses[i] || '' - inputHtml = '
' + - '' + - '
' - } + } - $question = $('
' + - '
' + - '
' + problem + '
' + - '
' + - '
' + - inputHtml + - '
' + - '
' - ).css('display', 'none') - - $('#quiz-form') - .append($question) - - // Show current question - $('#quiz-form') - .find('#question-' + currentQuestion) - .css('display', 'block') - - // Update progress bar - $('#progress') - .css('width', (responseCount / questions.length * 100) + '%') - } + $question = $('
' + + '
' + + '
' + problem + '
' + + '
' + + '
' + + inputHtml + + '
' + + '
' + ).css('display', 'none') - // Add button to submit response - $('#quiz') - .append('') + $('#quiz-form') + .append($question) - // Is case all questions have been responded - if (responseCount === questions.length) { - $('#submit-response').css('display', 'none') - $('#quiz').append('
Thank you for your responses.

') - $('#quiz').append('') - } + // Show current question + $('#quiz-form') + .find('#question-' + currentQuestion) + .css('display', 'block') - // Add a reset button that will redirect to quiz start - $resetButton = $('') - $resetButton.on('click', function() { - localStorage.removeItem('quiz') - location.reload(); - }) - $('#quiz').append($resetButton) - - // Actions on every response submission - $('#submit-response').on('click', function() { - const $inputs = $('[name^=question_' + currentQuestion + ']') - const question = questions[currentQuestion] - let response = responses[currentQuestion] - - // Behavior for each question type to add response to array of responses - switch (question.input.type) { - case 'checkbox': - case 'radio': - response = [] - $('[name=' + $inputs.attr('name') + ']:checked').each(function(i, input) { - response.push(input.value) - }) - response = response.length ? response : null - break - case 'inputs': - response = [] - $inputs.each(function(i, input) { - response.push(input.value) - }) - break - default: - response = $inputs.val() + // Update progress bar + $('#progress') + .css('width', (responseCount / questions.length * 100) + '%') } + // Add button to submit response + $('#quiz') + .append('') + // Is case all questions have been responded + if (responseCount === questions.length) { + $('#submit-response').css('display', 'none') + $('#quiz').append('
Thank you for your responses.

') + $('#quiz').append('') + } - // Set the current responses counter - responses[currentQuestion] = response - let responseCount = 0 - for (let i = 0; i < responses.length; i++) { - let question = questions[i] - let response = responses[i] + // Add a reset button that will redirect to quiz start + $resetButton = $('') + $resetButton.on('click', function() { + localStorage.removeItem('quiz') + location.reload(); + }) + $('#quiz').append($resetButton) + + // Actions on every response submission + $('#submit-response').on('click', function() { + const $inputs = $('[name^=question_' + currentQuestion + ']') + const question = questions[currentQuestion] + let response = responses[currentQuestion] + + // Behavior for each question type to add response to array of responses switch (question.input.type) { case 'checkbox': case 'radio': + response = [] + $('[name=' + $inputs.attr('name') + ']:checked').each(function(i, input) { + response.push(input.value) + }) + response = response.length ? response : null + break case 'inputs': - responseCount += !!response && !!response.join('') + response = [] + $inputs.each(function(i, input) { + response.push(input.value) + }) break default: - responseCount += !!response + response = $inputs.val() } - } - // Update progress bar - $('#progress') - .css('width', (responseCount / questions.length * 100) + '%') + + + // Set the current responses counter + responses[currentQuestion] = response + let responseCount = 0 + for (let i = 0; i < responses.length; i++) { + let question = questions[i] + let response = responses[i] + switch (question.input.type) { + case 'checkbox': + case 'radio': + case 'inputs': + responseCount += !!response && !!response.join('') + break + default: + responseCount += !!response + } + } + + // Update progress bar + $('#progress') + .css('width', (responseCount / questions.length * 100) + '%') - // Check if question had a valid answer - let isQuestionAnswered = !!response - if (response && response.length) { - for (j = 0; j < response.length; j++) { - if (!response[j]) { - isQuestionAnswered = false + // Check if question had a valid answer + let isQuestionAnswered = !!response + if (response && response.length) { + for (j = 0; j < response.length; j++) { + if (!response[j]) { + isQuestionAnswered = false + } } } - } - if (!isQuestionAnswered) { - // Alert user of missing response - alert('You must give a response') - } else { + if (!isQuestionAnswered) { + // Alert user of missing response + alert('You must give a response') + } else { - // Display next question - $('#quiz-form') - .find('#question-' + currentQuestion).css('display', 'none') + // Display next question + $('#quiz-form') + .find('#question-' + currentQuestion).css('display', 'none') - $('#quiz-form') - .find('#question-' + ++currentQuestion).css('display', 'block') + $('#quiz-form') + .find('#question-' + ++currentQuestion).css('display', 'block') - // If it was the las question, display final message - if (responseCount === questions.length) { - $('#submit-response').css('display', 'none') - $('#quiz').append('
Thank you for your responses.

') - $('#quiz').append('') + // If it was the las question, display final message + if (responseCount === questions.length) { + $('#submit-response').css('display', 'none') + $('#quiz').append('
Thank you for your responses.

') + $('#quiz').append('') + } } - } - // Save current state of the quiz - quizData.responses = responses - quizData.responseCount = responseCount - quizData.currentQuestion = currentQuestion - localStorage.setItem('quiz', JSON.stringify(quizData)) + // Save current state of the quiz + quizData.responses = responses + quizData.responseCount = responseCount + quizData.currentQuestion = currentQuestion + localStorage.setItem('quiz', JSON.stringify(quizData)) + }) }) -}) +})($, JSON, localStorage) From 9a7d4461b44fa596f4d4cb3e63d151d4ca19eb30 Mon Sep 17 00:00:00 2001 From: David Almeida Date: Mon, 30 Oct 2017 17:25:37 +0100 Subject: [PATCH 04/26] missing changes --- src/index.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/index.js b/src/index.js index 98004c4..ded669c 100644 --- a/src/index.js +++ b/src/index.js @@ -117,7 +117,7 @@ } // Add a reset button that will redirect to quiz start - $resetButton = $('') + const $resetButton = $('') $resetButton.on('click', function() { localStorage.removeItem('quiz') location.reload(); @@ -178,7 +178,7 @@ // Check if question had a valid answer let isQuestionAnswered = !!response if (response && response.length) { - for (j = 0; j < response.length; j++) { + for (let j = 0; j < response.length; j++) { if (!response[j]) { isQuestionAnswered = false } @@ -208,10 +208,7 @@ // Save current state of the quiz - quizData.responses = responses - quizData.responseCount = responseCount - quizData.currentQuestion = currentQuestion - localStorage.setItem('quiz', JSON.stringify(quizData)) + localStorage.setItem('quiz', JSON.stringify({responses, responseCount, currentQuestion})) }) }) })($, JSON, localStorage) From ae7e65eb0e4a104f845c3a51970eeda8eaf1096a Mon Sep 17 00:00:00 2001 From: David Almeida Date: Mon, 30 Oct 2017 17:27:43 +0100 Subject: [PATCH 05/26] reassign url --- src/index.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/index.js b/src/index.js index ded669c..c2710d0 100644 --- a/src/index.js +++ b/src/index.js @@ -1,11 +1,9 @@ (function($, JSON, localStorage){ - const options = { + const {url} = options = { url: 'data/quiz.json?' + Date.now() } - $.ajax({ - url: options.url - }).done(function(data) { + $.ajax({ url }).done(function(data) { let {questions} = data let quizData From 35289a3538424fd47090969c7553c034462bfcef Mon Sep 17 00:00:00 2001 From: David Almeida Date: Wed, 1 Nov 2017 12:13:34 +0100 Subject: [PATCH 06/26] convert strings to template strings --- src/index.js | 69 ++++++++++++++++++++++++---------------------------- 1 file changed, 32 insertions(+), 37 deletions(-) diff --git a/src/index.js b/src/index.js index c2710d0..39f5398 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,6 @@ (function($, JSON, localStorage){ const {url} = options = { - url: 'data/quiz.json?' + Date.now() + url: `data/quiz.json?${Date.now()}` } $.ajax({ url }).done(function(data) { @@ -16,13 +16,13 @@ // Append the progress bar to DOM $('body') - .append('
' + - '
 
' + - '
') + .append(`
+
 
+
`) // Append title and form to quiz $('#quiz') - .append('

' + data.title + '

') + .append(`

${data.title}

`) .append('
') // For each question of the json, @@ -43,12 +43,12 @@ const {[j]:option} = options const checked = !!responses[i] && responses[i].includes(option.label) ? 'checked' : '' - inputHtml += '
' + - '
' + - '' + - '' + - '
' + - '
' + inputHtml += `
+
+ + +
+
` } inputHtml += '
' break @@ -56,18 +56,18 @@ // Set of inputs (composed response) case 'inputs': inputHtml = '' - for (j = 0; j < options.length; j++) { + for (let j = 0; j < options.length; j++) { const {[j]:option} = options const value = responses[i] && responses[i][j] || '' - inputHtml += '' + - '' + - '' + - '' + - '' + - '' + inputHtml += ` + + + + + ` } inputHtml += '
' + - '' + - '
 
+ +
 
' break @@ -75,19 +75,15 @@ // Default: simple input default: const value = responses[i] || '' - inputHtml = '
' + - '' + - '
' + inputHtml = `
+ +
` } - $question = $('
' + - '
' + - '
' + problem + '
' + - '
' + - '
' + - inputHtml + - '
' + - '
' + $question = $(`
+
${problem}
+
${inputHtml}
+
` ).css('display', 'none') $('#quiz-form') @@ -95,7 +91,7 @@ // Show current question $('#quiz-form') - .find('#question-' + currentQuestion) + .find(`#question-${currentQuestion}`) .css('display', 'block') // Update progress bar @@ -124,7 +120,7 @@ // Actions on every response submission $('#submit-response').on('click', function() { - const $inputs = $('[name^=question_' + currentQuestion + ']') + const $inputs = $(`[name^=question_${currentQuestion}`) const question = questions[currentQuestion] let response = responses[currentQuestion] @@ -133,7 +129,7 @@ case 'checkbox': case 'radio': response = [] - $('[name=' + $inputs.attr('name') + ']:checked').each(function(i, input) { + $(`[name=${$inputs.attr('name')}]:checked`).each(function(i, input) { response.push(input.value) }) response = response.length ? response : null @@ -190,11 +186,11 @@ // Display next question $('#quiz-form') - .find('#question-' + currentQuestion).css('display', 'none') + .find(`#question-${currentQuestion}`).css('display', 'none') $('#quiz-form') - .find('#question-' + ++currentQuestion).css('display', 'block') + .find(`#question-${++currentQuestion}`).css('display', 'block') // If it was the las question, display final message if (responseCount === questions.length) { @@ -204,7 +200,6 @@ } } - // Save current state of the quiz localStorage.setItem('quiz', JSON.stringify({responses, responseCount, currentQuestion})) }) From 84e7ae26dfdfeec1deba7207d457bca03ed92f6a Mon Sep 17 00:00:00 2001 From: David Almeida Date: Mon, 20 Nov 2017 15:58:13 +0100 Subject: [PATCH 07/26] function for multiple options fields --- src/index.js | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/index.js b/src/index.js index 39f5398..31c2302 100644 --- a/src/index.js +++ b/src/index.js @@ -3,6 +3,24 @@ url: `data/quiz.json?${Date.now()}` } + const getOptionsMarkup = type => id => options => response => { + return '
' + + options + .map(({label}, j) => { + const checked = !!response && response.includes(option.label) ? 'checked' : '' + const optionId = `${id}_${j}` + return `
+
+ + +
+
` + }).join('') + + '
' + } + const getCheckboxesMarkup = getOptionsMarkup('checkbox') + const getRadiosMarkup = getOptionsMarkup('radio') + $.ajax({ url }).done(function(data) { let {questions} = data let quizData @@ -29,28 +47,17 @@ for (let i = 0; i < questions.length; i++) { questions[i].input = questions[i].input || { type:'input' } let {problem, input, input: {type, options}} = questions[i] + let response = responses[i] let inputHtml - // Construct the input depending on question type switch (type) { // Multiple options case 'checkbox': + inputHtml = getCheckboxesMarkup(name)(options)(response) case 'radio': - inputHtml = '
' - for (j = 0; j < options.length; j++) { - const {[j]:option} = options - const checked = !!responses[i] && responses[i].includes(option.label) ? 'checked' : '' - - inputHtml += `
-
- - -
-
` - } - inputHtml += '
' + inputHtml = getRadiosMarkup(name)(options)(response) break // Set of inputs (composed response) From e99403c97aca6673d16bc649d225cd2e7770441d Mon Sep 17 00:00:00 2001 From: David Almeida Date: Mon, 20 Nov 2017 16:06:21 +0100 Subject: [PATCH 08/26] function for multiple inputs fields --- src/index.js | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/src/index.js b/src/index.js index 31c2302..fe749fc 100644 --- a/src/index.js +++ b/src/index.js @@ -6,8 +6,8 @@ const getOptionsMarkup = type => id => options => response => { return '
' + options - .map(({label}, j) => { - const checked = !!response && response.includes(option.label) ? 'checked' : '' + .map(({label}, j) => { + const checked = !!response && response.includes(label) ? 'checked' : '' const optionId = `${id}_${j}` return `
@@ -18,6 +18,23 @@ }).join('') + '
' } + + const getInputsOptionsMarkup = id => options => response => { + return '' + + options.map(({label}, j) => { + const optionId = `${id}_${j}` + const value = response && response[j] || '' + return ` + + + + + ` }).join('') + + '
+ +
 
' + } + const getCheckboxesMarkup = getOptionsMarkup('checkbox') const getRadiosMarkup = getOptionsMarkup('radio') @@ -50,6 +67,7 @@ let response = responses[i] let inputHtml + // Construct the input depending on question type switch (type) { @@ -62,21 +80,7 @@ // Set of inputs (composed response) case 'inputs': - inputHtml = '' - for (let j = 0; j < options.length; j++) { - const {[j]:option} = options - const value = responses[i] && responses[i][j] || '' - - inputHtml += ` - - - - - ` - } - inputHtml += '
- -
 
' + inputHtml = getInputsOptionsMarkup(name)(options)(response) break // Default: simple input From 09a0389f85384214270af93cc42a9023f18abec7 Mon Sep 17 00:00:00 2001 From: David Almeida Date: Mon, 20 Nov 2017 16:35:41 +0100 Subject: [PATCH 09/26] function for input fields --- src/index.js | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/index.js b/src/index.js index fe749fc..73cb02a 100644 --- a/src/index.js +++ b/src/index.js @@ -3,7 +3,7 @@ url: `data/quiz.json?${Date.now()}` } - const getOptionsMarkup = type => id => options => response => { + const getOptionsMarkup = type => options => id => response => { return '
' + options .map(({label}, j) => { @@ -11,15 +11,15 @@ const optionId = `${id}_${j}` return `
- +
` }).join('') + '
' } - - const getInputsOptionsMarkup = id => options => response => { + + const getInputsOptionsMarkup = options => id => response => { return '' + options.map(({label}, j) => { const optionId = `${id}_${j}` @@ -35,6 +35,10 @@ + '
' } + const getInputMarkup = id => (response='') => `
+ +
` + const getCheckboxesMarkup = getOptionsMarkup('checkbox') const getRadiosMarkup = getOptionsMarkup('radio') @@ -65,30 +69,25 @@ questions[i].input = questions[i].input || { type:'input' } let {problem, input, input: {type, options}} = questions[i] let response = responses[i] + let name = `question_${i}` let inputHtml - // Construct the input depending on question type switch (type) { - // Multiple options case 'checkbox': - inputHtml = getCheckboxesMarkup(name)(options)(response) + inputHtml = getCheckboxesMarkup(options)(name)(response) + break case 'radio': - inputHtml = getRadiosMarkup(name)(options)(response) + inputHtml = getRadiosMarkup(options)(name)(response) break - // Set of inputs (composed response) case 'inputs': - inputHtml = getInputsOptionsMarkup(name)(options)(response) + inputHtml = getInputsOptionsMarkup(options)(name)(response) break - // Default: simple input default: - const value = responses[i] || '' - inputHtml = `
- -
` + inputHtml = getInputMarkup(name)(response) } $question = $(`
From 06bcc199ae9f3673ff83c46bf29349dda000871c Mon Sep 17 00:00:00 2001 From: David Almeida Date: Wed, 22 Nov 2017 12:00:24 +0100 Subject: [PATCH 10/26] move quiz storage to functions --- src/index.js | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/index.js b/src/index.js index 73cb02a..b4e430b 100644 --- a/src/index.js +++ b/src/index.js @@ -12,7 +12,7 @@ return `
- +
` }).join('') @@ -42,15 +42,12 @@ const getCheckboxesMarkup = getOptionsMarkup('checkbox') const getRadiosMarkup = getOptionsMarkup('radio') + const getQuiz = () => JSON.parse(localStorage.getItem('quiz') || null) || {} + const setQuiz = data => localStorage.setItem('quiz', JSON.stringify(data)) + $.ajax({ url }).done(function(data) { let {questions} = data - let quizData - - // Load data from past reponses - try { - quizData = JSON.parse(localStorage.getItem('quiz')) || {} - } catch (e) {} - let {responses=[], currentQuestion=0, responseCount=0} = quizData + let {responses=[], currentQuestion=0, responseCount=0} = getQuiz() // Append the progress bar to DOM @@ -211,7 +208,7 @@ } // Save current state of the quiz - localStorage.setItem('quiz', JSON.stringify({responses, responseCount, currentQuestion})) + setQuiz({responses, responseCount, currentQuestion}) }) }) })($, JSON, localStorage) From ae0998bdb7fb263d27512fcb7b5e7380a42d1264 Mon Sep 17 00:00:00 2001 From: David Almeida Date: Wed, 22 Nov 2017 12:18:39 +0100 Subject: [PATCH 11/26] move quiz DOM setup to functions --- src/index.js | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/index.js b/src/index.js index b4e430b..2163893 100644 --- a/src/index.js +++ b/src/index.js @@ -44,22 +44,21 @@ const getQuiz = () => JSON.parse(localStorage.getItem('quiz') || null) || {} const setQuiz = data => localStorage.setItem('quiz', JSON.stringify(data)) + const setupQuizElement = (quizContainer, title) => { + $(document.body) + .append(`
+
 
+
`) + $(quizContainer) + .append(`

${title}

`) + .append('
') + } $.ajax({ url }).done(function(data) { let {questions} = data let {responses=[], currentQuestion=0, responseCount=0} = getQuiz() - - // Append the progress bar to DOM - $('body') - .append(`
-
 
-
`) - - // Append title and form to quiz - $('#quiz') - .append(`

${data.title}

`) - .append('
') + setupQuizElement(document.getElementById('quiz'), data.title) // For each question of the json, for (let i = 0; i < questions.length; i++) { From da9703b293d70a13f5f8d8babff7c7e94bb1d833 Mon Sep 17 00:00:00 2001 From: David Almeida Date: Wed, 22 Nov 2017 12:19:14 +0100 Subject: [PATCH 12/26] move progress to a func --- src/index.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/index.js b/src/index.js index 2163893..517e290 100644 --- a/src/index.js +++ b/src/index.js @@ -53,6 +53,9 @@ .append(`

${title}

`) .append('
') } + const updateProgress = questions => responses => + $('#progress') + .css('width', (responses / questions * 100) + '%') $.ajax({ url }).done(function(data) { let {questions} = data @@ -100,9 +103,7 @@ .find(`#question-${currentQuestion}`) .css('display', 'block') - // Update progress bar - $('#progress') - .css('width', (responseCount / questions.length * 100) + '%') + updateProgress(questions.length)(responseCount) } // Add button to submit response @@ -169,11 +170,7 @@ } } - // Update progress bar - $('#progress') - .css('width', (responseCount / questions.length * 100) + '%') - - + updateProgress(questions.length)(responseCount) // Check if question had a valid answer let isQuestionAnswered = !!response From ac25a5860d0b54caadc618cc8b5a1a50317cde6e Mon Sep 17 00:00:00 2001 From: David Almeida Date: Wed, 22 Nov 2017 12:33:46 +0100 Subject: [PATCH 13/26] optimize updateProgress with partial application --- src/index.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index 517e290..048ca56 100644 --- a/src/index.js +++ b/src/index.js @@ -60,6 +60,7 @@ $.ajax({ url }).done(function(data) { let {questions} = data let {responses=[], currentQuestion=0, responseCount=0} = getQuiz() + const updateQuizProgress = updateProgress(questions.length) setupQuizElement(document.getElementById('quiz'), data.title) @@ -103,7 +104,7 @@ .find(`#question-${currentQuestion}`) .css('display', 'block') - updateProgress(questions.length)(responseCount) + updateQuizProgress(responseCount) } // Add button to submit response @@ -170,7 +171,7 @@ } } - updateProgress(questions.length)(responseCount) + updateQuizProgress(responseCount) // Check if question had a valid answer let isQuestionAnswered = !!response From 9bbc3bde750b31c59bf30e2e3a264fd185d72b16 Mon Sep 17 00:00:00 2001 From: David Almeida Date: Wed, 22 Nov 2017 13:02:11 +0100 Subject: [PATCH 14/26] move question markup creation to function --- src/index.js | 59 ++++++++++++++++++++++++++-------------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/src/index.js b/src/index.js index 048ca56..9358072 100644 --- a/src/index.js +++ b/src/index.js @@ -53,6 +53,34 @@ .append(`

${title}

`) .append('
') } + const createQuestionElement = ({problem, input, input: {type, options}}) => name => response => { + let inputHtml + + // Construct the input depending on question type + switch (type) { + // Multiple options + case 'checkbox': + inputHtml = getCheckboxesMarkup(options)(name)(response) + break + case 'radio': + inputHtml = getRadiosMarkup(options)(name)(response) + break + // Set of inputs (composed response) + case 'inputs': + inputHtml = getInputsOptionsMarkup(options)(name)(response) + break + // Default: simple input + default: + inputHtml = getInputMarkup(name)(response) + } + + return $(`
+
${problem}
+
${inputHtml}
+
` + ).css('display', 'none').get(0) + } + const updateProgress = questions => responses => $('#progress') .css('width', (responses / questions * 100) + '%') @@ -67,41 +95,14 @@ // For each question of the json, for (let i = 0; i < questions.length; i++) { questions[i].input = questions[i].input || { type:'input' } - let {problem, input, input: {type, options}} = questions[i] - let response = responses[i] - let name = `question_${i}` - let inputHtml - - // Construct the input depending on question type - switch (type) { - // Multiple options - case 'checkbox': - inputHtml = getCheckboxesMarkup(options)(name)(response) - break - case 'radio': - inputHtml = getRadiosMarkup(options)(name)(response) - break - // Set of inputs (composed response) - case 'inputs': - inputHtml = getInputsOptionsMarkup(options)(name)(response) - break - // Default: simple input - default: - inputHtml = getInputMarkup(name)(response) - } - - $question = $(`
-
${problem}
-
${inputHtml}
-
` - ).css('display', 'none') + $question = createQuestionElement(questions[i])(`question_${i}`)(responses[i]) $('#quiz-form') .append($question) // Show current question $('#quiz-form') - .find(`#question-${currentQuestion}`) + .find(`#question_${currentQuestion}`) .css('display', 'block') updateQuizProgress(responseCount) From ba19d20fbac23a1e3698ed64593248e7e146b770 Mon Sep 17 00:00:00 2001 From: David Almeida Date: Wed, 22 Nov 2017 13:11:38 +0100 Subject: [PATCH 15/26] move question displaying to func --- src/index.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 9358072..892e7f0 100644 --- a/src/index.js +++ b/src/index.js @@ -78,13 +78,24 @@
${problem}
${inputHtml}
` - ).css('display', 'none').get(0) + ).get(0) } + const appendToQuizForm = element => $('#quiz-form').append(element) const updateProgress = questions => responses => $('#progress') .css('width', (responses / questions * 100) + '%') + const updateQuizViewStatus = current => { + $('#quiz-form') + .find(`[id^=question_]`) + .css('display', 'none') + $('#quiz-form') + .find(`#question_${current}`) + .css('display', 'block') + } + + $.ajax({ url }).done(function(data) { let {questions} = data let {responses=[], currentQuestion=0, responseCount=0} = getQuiz() From bf0fb2d02f5b2561f87ab42211c4a6241ce0d330 Mon Sep 17 00:00:00 2001 From: David Almeida Date: Wed, 22 Nov 2017 14:43:51 +0100 Subject: [PATCH 16/26] move reset and submit buttons to setup funcs --- src/index.js | 192 ++++++++++++++++++++++++--------------------------- 1 file changed, 90 insertions(+), 102 deletions(-) diff --git a/src/index.js b/src/index.js index 892e7f0..ba84d3c 100644 --- a/src/index.js +++ b/src/index.js @@ -45,13 +45,21 @@ const getQuiz = () => JSON.parse(localStorage.getItem('quiz') || null) || {} const setQuiz = data => localStorage.setItem('quiz', JSON.stringify(data)) const setupQuizElement = (quizContainer, title) => { + const $resetButton = $('') + const $submitButton = $('') + $resetButton.on('click', resetQuiz) + $submitButton.on('click', submitResponse) + + $(quizContainer) + .append(`

${title}

`) + .append('
') + .append($submitButton) + .append($resetButton) + $(document.body) .append(`
 
`) - $(quizContainer) - .append(`

${title}

`) - .append('
') } const createQuestionElement = ({problem, input, input: {type, options}}) => name => response => { let inputHtml @@ -95,129 +103,109 @@ .css('display', 'block') } + const setupQuestions = questions => responses => { + const defaultInput = { input: {type:'input'} } + questions + .map(question => question.input ? question : Object.assign({}, defaultInput, question)) + .map((question, i) => createQuestionElement(question)(`question_${i}`)(responses[i])) + .map(appendToQuizForm) + } - $.ajax({ url }).done(function(data) { - let {questions} = data - let {responses=[], currentQuestion=0, responseCount=0} = getQuiz() - const updateQuizProgress = updateProgress(questions.length) - - setupQuizElement(document.getElementById('quiz'), data.title) - - // For each question of the json, - for (let i = 0; i < questions.length; i++) { - questions[i].input = questions[i].input || { type:'input' } - $question = createQuestionElement(questions[i])(`question_${i}`)(responses[i]) + const resetQuiz = () => { + localStorage.removeItem('quiz') + location.reload(); + } - $('#quiz-form') - .append($question) + const submitResponse = function() { + const $inputs = $(`[name^=question_${currentQuestion}`) + const question = questions[currentQuestion] + let response = responses[currentQuestion] - // Show current question - $('#quiz-form') - .find(`#question_${currentQuestion}`) - .css('display', 'block') - updateQuizProgress(responseCount) + // Behavior for each question type to add response to array of responses + switch (question.input.type) { + case 'checkbox': + case 'radio': + response = [] + $(`[name=${$inputs.attr('name')}]:checked`).each(function(i, input) { + response.push(input.value) + }) + response = response.length ? response : null + break + case 'inputs': + response = [] + $inputs.each(function(i, input) { + response.push(input.value) + }) + break + default: + response = $inputs.val() } - // Add button to submit response - $('#quiz') - .append('') - // Is case all questions have been responded - if (responseCount === questions.length) { - $('#submit-response').css('display', 'none') - $('#quiz').append('
Thank you for your responses.

') - $('#quiz').append('') - } - // Add a reset button that will redirect to quiz start - const $resetButton = $('') - $resetButton.on('click', function() { - localStorage.removeItem('quiz') - location.reload(); - }) - $('#quiz').append($resetButton) - - // Actions on every response submission - $('#submit-response').on('click', function() { - const $inputs = $(`[name^=question_${currentQuestion}`) - const question = questions[currentQuestion] - let response = responses[currentQuestion] - - // Behavior for each question type to add response to array of responses + // Set the current responses counter + responses[currentQuestion] = response + let responseCount = 0 + for (let i = 0; i < responses.length; i++) { + let question = questions[i] + let response = responses[i] switch (question.input.type) { case 'checkbox': case 'radio': - response = [] - $(`[name=${$inputs.attr('name')}]:checked`).each(function(i, input) { - response.push(input.value) - }) - response = response.length ? response : null - break case 'inputs': - response = [] - $inputs.each(function(i, input) { - response.push(input.value) - }) + responseCount += !!response && !!response.join('') break default: - response = $inputs.val() + responseCount += !!response } + } + updateQuizProgress(responseCount) - - // Set the current responses counter - responses[currentQuestion] = response - let responseCount = 0 - for (let i = 0; i < responses.length; i++) { - let question = questions[i] - let response = responses[i] - switch (question.input.type) { - case 'checkbox': - case 'radio': - case 'inputs': - responseCount += !!response && !!response.join('') - break - default: - responseCount += !!response + // Check if question had a valid answer + let isQuestionAnswered = !!response + if (response && response.length) { + for (let j = 0; j < response.length; j++) { + if (!response[j]) { + isQuestionAnswered = false } } + } - updateQuizProgress(responseCount) - - // Check if question had a valid answer - let isQuestionAnswered = !!response - if (response && response.length) { - for (let j = 0; j < response.length; j++) { - if (!response[j]) { - isQuestionAnswered = false - } - } + if (!isQuestionAnswered) { + // Alert user of missing response + alert('You must give a response') + } else { + updateQuizViewStatus(++currentQuestion) + + // If it was the las question, display final message + if (responseCount === questions.length) { + $('#submit-response').css('display', 'none') + $('#quiz').append('
Thank you for your responses.

') + $('#quiz').append('') } + } - if (!isQuestionAnswered) { - // Alert user of missing response - alert('You must give a response') - } else { - - // Display next question - $('#quiz-form') - .find(`#question-${currentQuestion}`).css('display', 'none') - + // Save current state of the quiz + setQuiz({responses, responseCount, currentQuestion}) + } - $('#quiz-form') - .find(`#question-${++currentQuestion}`).css('display', 'block') + $.ajax({ url }).done(function(data) { + let {questions} = data + let {responses=[], currentQuestion=0, responseCount=0} = getQuiz() + const updateQuizProgress = updateProgress(questions.length) - // If it was the las question, display final message - if (responseCount === questions.length) { - $('#submit-response').css('display', 'none') - $('#quiz').append('
Thank you for your responses.

') - $('#quiz').append('') - } - } + setupQuizElement(document.getElementById('quiz'), data.title) + setupQuestions(questions)(responses) + updateQuizViewStatus(currentQuestion) + updateQuizProgress(responseCount) - // Save current state of the quiz - setQuiz({responses, responseCount, currentQuestion}) - }) + // Is case all questions have been responded + if (responseCount === questions.length) { + $('#submit-response').css('display', 'none') + $('#quiz').append('
Thank you for your responses.

') + $('#quiz').append('') + } }) })($, JSON, localStorage) From 4a6ceaa94d5611252bce1fd6e6c65de69ab91714 Mon Sep 17 00:00:00 2001 From: David Almeida Date: Wed, 22 Nov 2017 15:00:18 +0100 Subject: [PATCH 17/26] move end message to quiz status func --- src/index.js | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/index.js b/src/index.js index ba84d3c..17ab2af 100644 --- a/src/index.js +++ b/src/index.js @@ -94,13 +94,21 @@ $('#progress') .css('width', (responses / questions * 100) + '%') - const updateQuizViewStatus = current => { + const updateQuizViewStatus = questions => current => { $('#quiz-form') .find(`[id^=question_]`) .css('display', 'none') $('#quiz-form') .find(`#question_${current}`) .css('display', 'block') + + // Is case all questions have been responded + if (questions <= current ) { + $('#submit-response').css('display', 'none') + $('#quiz') + .append('
Thank you for your responses.

') + .append('') + } } const setupQuestions = questions => responses => { @@ -177,14 +185,7 @@ // Alert user of missing response alert('You must give a response') } else { - updateQuizViewStatus(++currentQuestion) - - // If it was the las question, display final message - if (responseCount === questions.length) { - $('#submit-response').css('display', 'none') - $('#quiz').append('
Thank you for your responses.

') - $('#quiz').append('') - } + updateQuizViewStatus(questions.length)(++currentQuestion) } // Save current state of the quiz @@ -198,14 +199,7 @@ setupQuizElement(document.getElementById('quiz'), data.title) setupQuestions(questions)(responses) - updateQuizViewStatus(currentQuestion) + updateQuizViewStatus(questions.length)(currentQuestion) updateQuizProgress(responseCount) - - // Is case all questions have been responded - if (responseCount === questions.length) { - $('#submit-response').css('display', 'none') - $('#quiz').append('
Thank you for your responses.

') - $('#quiz').append('') - } }) })($, JSON, localStorage) From 7e59b8aa0e5635abb6c68ebdb4956f40b9f7f315 Mon Sep 17 00:00:00 2001 From: David Almeida Date: Tue, 28 Nov 2017 11:46:46 +0100 Subject: [PATCH 18/26] refactor submitResponse func --- src/index.js | 52 ++++++++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/src/index.js b/src/index.js index 17ab2af..6802e45 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,5 @@ (function($, JSON, localStorage){ + console.clear() const {url} = options = { url: `data/quiz.json?${Date.now()}` } @@ -44,11 +45,11 @@ const getQuiz = () => JSON.parse(localStorage.getItem('quiz') || null) || {} const setQuiz = data => localStorage.setItem('quiz', JSON.stringify(data)) - const setupQuizElement = (quizContainer, title) => { + const setupQuizElement = ({title, questions}) => (quizContainer) => { const $resetButton = $('') const $submitButton = $('') $resetButton.on('click', resetQuiz) - $submitButton.on('click', submitResponse) + $submitButton.on('click', submitResponse(questions)) $(quizContainer) .append(`

${title}

`) @@ -96,7 +97,7 @@ const updateQuizViewStatus = questions => current => { $('#quiz-form') - .find(`[id^=question_]`) + .find(`.card[id^=question_]`) .css('display', 'none') $('#quiz-form') .find(`#question_${current}`) @@ -124,41 +125,45 @@ location.reload(); } - const submitResponse = function() { - const $inputs = $(`[name^=question_${currentQuestion}`) - const question = questions[currentQuestion] - let response = responses[currentQuestion] + const getInputsByName = form => { + const inputs = Array.from(form) + return name => inputs.filter( input => input.name.includes(name) ) + } + + const submitResponse = questions => () => { + let {responses=[], currentQuestion=0} = getQuiz() + const {input:{type}={}} = questions[currentQuestion] + const getFormInputs = getInputsByName(document.getElementById('quiz-form')) + const inputs = getFormInputs(`question_${currentQuestion}`) + // Behavior for each question type to add response to array of responses - switch (question.input.type) { + switch (type) { case 'checkbox': case 'radio': - response = [] - $(`[name=${$inputs.attr('name')}]:checked`).each(function(i, input) { - response.push(input.value) - }) + response = inputs + .filter(({checked}) => checked) + .map(({value}) => value) response = response.length ? response : null break case 'inputs': - response = [] - $inputs.each(function(i, input) { - response.push(input.value) - }) + response = inputs + .map(({value}) => value) break default: - response = $inputs.val() + response = inputs + .map(({value}) => value)[0] } - - // Set the current responses counter responses[currentQuestion] = response let responseCount = 0 for (let i = 0; i < responses.length; i++) { let question = questions[i] let response = responses[i] - switch (question.input.type) { + + switch (question.input && question.input.type) { case 'checkbox': case 'radio': case 'inputs': @@ -169,7 +174,7 @@ } } - updateQuizProgress(responseCount) + updateProgress(questions.length)(responseCount) // Check if question had a valid answer let isQuestionAnswered = !!response @@ -195,11 +200,10 @@ $.ajax({ url }).done(function(data) { let {questions} = data let {responses=[], currentQuestion=0, responseCount=0} = getQuiz() - const updateQuizProgress = updateProgress(questions.length) - setupQuizElement(document.getElementById('quiz'), data.title) + setupQuizElement(data)(document.getElementById('quiz')) setupQuestions(questions)(responses) updateQuizViewStatus(questions.length)(currentQuestion) - updateQuizProgress(responseCount) + updateProgress(questions.length)(responseCount) }) })($, JSON, localStorage) From 9dc7f51a5d3ad37392cebf4f8f23b7cbed08c05c Mon Sep 17 00:00:00 2001 From: David Almeida Date: Tue, 28 Nov 2017 15:28:38 +0100 Subject: [PATCH 19/26] Pass response processing to functional --- src/index.js | 61 +++++++++++----------------------------------------- 1 file changed, 13 insertions(+), 48 deletions(-) diff --git a/src/index.js b/src/index.js index 6802e45..f4c0cc3 100644 --- a/src/index.js +++ b/src/index.js @@ -130,71 +130,36 @@ return name => inputs.filter( input => input.name.includes(name) ) } - + const isValidResponse = response => !!response // has a value + && !!Array.from(response).length // not an empty array + && !!Array.from(response).reduce((value, item) => value && !!item, true) // no value is empty const submitResponse = questions => () => { let {responses=[], currentQuestion=0} = getQuiz() - const {input:{type}={}} = questions[currentQuestion] const getFormInputs = getInputsByName(document.getElementById('quiz-form')) const inputs = getFormInputs(`question_${currentQuestion}`) - - // Behavior for each question type to add response to array of responses - switch (type) { - case 'checkbox': - case 'radio': - response = inputs - .filter(({checked}) => checked) - .map(({value}) => value) - response = response.length ? response : null - break - case 'inputs': - response = inputs - .map(({value}) => value) - break - default: - response = inputs - .map(({value}) => value)[0] - } + const response = inputs + .filter(({checked, type}) => type === 'text' || checked ) // has checked and it's false + .map(({value}) => value) // map to actual values // Set the current responses counter - responses[currentQuestion] = response - let responseCount = 0 - for (let i = 0; i < responses.length; i++) { - let question = questions[i] - let response = responses[i] - - switch (question.input && question.input.type) { - case 'checkbox': - case 'radio': - case 'inputs': - responseCount += !!response && !!response.join('') - break - default: - responseCount += !!response - } - } + responses.push(response) - updateProgress(questions.length)(responseCount) + // Count valid responses + const responseCount = responses + .reduce( ((value, response) => value + isValidResponse(response)), 0 ) // Check if question had a valid answer - let isQuestionAnswered = !!response - if (response && response.length) { - for (let j = 0; j < response.length; j++) { - if (!response[j]) { - isQuestionAnswered = false - } - } - } + const isQuestionAnswered = isValidResponse(response) if (!isQuestionAnswered) { // Alert user of missing response alert('You must give a response') } else { updateQuizViewStatus(questions.length)(++currentQuestion) + updateProgress(questions.length)(responseCount) + setQuiz({responses, responseCount, currentQuestion}) } - - // Save current state of the quiz - setQuiz({responses, responseCount, currentQuestion}) } $.ajax({ url }).done(function(data) { From dc9722ee2952dbd2dc5fbdaba8d99a5e021c9f93 Mon Sep 17 00:00:00 2001 From: David Almeida Date: Tue, 28 Nov 2017 15:32:55 +0100 Subject: [PATCH 20/26] Remove unnecessary response count from storage --- src/index.js | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/index.js b/src/index.js index f4c0cc3..86cbe18 100644 --- a/src/index.js +++ b/src/index.js @@ -134,6 +134,9 @@ && !!Array.from(response).length // not an empty array && !!Array.from(response).reduce((value, item) => value && !!item, true) // no value is empty + const countValidResponses = responses => responses + .reduce( ((value, response) => value + isValidResponse(response)), 0 ) + const submitResponse = questions => () => { let {responses=[], currentQuestion=0} = getQuiz() const getFormInputs = getInputsByName(document.getElementById('quiz-form')) @@ -145,30 +148,24 @@ // Set the current responses counter responses.push(response) - // Count valid responses - const responseCount = responses - .reduce( ((value, response) => value + isValidResponse(response)), 0 ) - - // Check if question had a valid answer - const isQuestionAnswered = isValidResponse(response) - - if (!isQuestionAnswered) { + if (!isValidResponse(response)) { // Alert user of missing response alert('You must give a response') } else { + // Count valid responses updateQuizViewStatus(questions.length)(++currentQuestion) - updateProgress(questions.length)(responseCount) - setQuiz({responses, responseCount, currentQuestion}) + updateProgress(questions.length)(countValidResponses(responses)) + setQuiz({responses, currentQuestion}) } } $.ajax({ url }).done(function(data) { let {questions} = data - let {responses=[], currentQuestion=0, responseCount=0} = getQuiz() + let {responses=[], currentQuestion=0} = getQuiz() setupQuizElement(data)(document.getElementById('quiz')) setupQuestions(questions)(responses) updateQuizViewStatus(questions.length)(currentQuestion) - updateProgress(questions.length)(responseCount) + updateProgress(questions.length)(countValidResponses(responses)) }) })($, JSON, localStorage) From 3ba74a523c135d3ecf8a7f699bf0f2541355363b Mon Sep 17 00:00:00 2001 From: David Almeida Date: Tue, 28 Nov 2017 20:19:20 +0100 Subject: [PATCH 21/26] base all partial applications on questions array --- src/index.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/index.js b/src/index.js index 86cbe18..7977e6b 100644 --- a/src/index.js +++ b/src/index.js @@ -93,7 +93,7 @@ const updateProgress = questions => responses => $('#progress') - .css('width', (responses / questions * 100) + '%') + .css('width', (responses / questions.length * 100) + '%') const updateQuizViewStatus = questions => current => { $('#quiz-form') @@ -104,7 +104,7 @@ .css('display', 'block') // Is case all questions have been responded - if (questions <= current ) { + if (questions.length <= current ) { $('#submit-response').css('display', 'none') $('#quiz') .append('
Thank you for your responses.

') @@ -153,8 +153,8 @@ alert('You must give a response') } else { // Count valid responses - updateQuizViewStatus(questions.length)(++currentQuestion) - updateProgress(questions.length)(countValidResponses(responses)) + updateQuizViewStatus(questions)(++currentQuestion) + updateProgress(questions)(countValidResponses(responses)) setQuiz({responses, currentQuestion}) } } @@ -165,7 +165,7 @@ setupQuizElement(data)(document.getElementById('quiz')) setupQuestions(questions)(responses) - updateQuizViewStatus(questions.length)(currentQuestion) - updateProgress(questions.length)(countValidResponses(responses)) + updateQuizViewStatus(questions)(currentQuestion) + updateProgress(questions)(countValidResponses(responses)) }) })($, JSON, localStorage) From a3d4e9ae10d22918d34cf5e6c3683444b50a9845 Mon Sep 17 00:00:00 2001 From: David Almeida Date: Tue, 28 Nov 2017 20:48:45 +0100 Subject: [PATCH 22/26] Remove unnecesaary currentQuestion variable --- src/index.js | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/index.js b/src/index.js index 7977e6b..bba26a2 100644 --- a/src/index.js +++ b/src/index.js @@ -91,11 +91,15 @@ } const appendToQuizForm = element => $('#quiz-form').append(element) - const updateProgress = questions => responses => + const updateProgress = questions => responses => { + const responseCount = countValidResponses(responses) $('#progress') - .css('width', (responses / questions.length * 100) + '%') + .css('width', (responseCount / questions.length * 100) + '%') + } + + const updateQuizViewStatus = questions => responses => { + const current = countValidResponses(responses) - const updateQuizViewStatus = questions => current => { $('#quiz-form') .find(`.card[id^=question_]`) .css('display', 'none') @@ -138,7 +142,8 @@ .reduce( ((value, response) => value + isValidResponse(response)), 0 ) const submitResponse = questions => () => { - let {responses=[], currentQuestion=0} = getQuiz() + let {responses=[]} = getQuiz() + const currentQuestion = responses.length const getFormInputs = getInputsByName(document.getElementById('quiz-form')) const inputs = getFormInputs(`question_${currentQuestion}`) const response = inputs @@ -153,19 +158,19 @@ alert('You must give a response') } else { // Count valid responses - updateQuizViewStatus(questions)(++currentQuestion) - updateProgress(questions)(countValidResponses(responses)) - setQuiz({responses, currentQuestion}) + updateQuizViewStatus(questions)(responses) + updateProgress(questions)(responses) + setQuiz({responses}) } } $.ajax({ url }).done(function(data) { let {questions} = data - let {responses=[], currentQuestion=0} = getQuiz() + let {responses=[]} = getQuiz() setupQuizElement(data)(document.getElementById('quiz')) setupQuestions(questions)(responses) - updateQuizViewStatus(questions)(currentQuestion) - updateProgress(questions)(countValidResponses(responses)) + updateQuizViewStatus(questions)(responses) + updateProgress(questions)(responses) }) })($, JSON, localStorage) From e0d5f78ea1505d2362353e86df591426f2396ff8 Mon Sep 17 00:00:00 2001 From: David Almeida Date: Tue, 28 Nov 2017 20:50:56 +0100 Subject: [PATCH 23/26] move updateProgress to updateQuizViewStatus --- src/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index bba26a2..56acd3c 100644 --- a/src/index.js +++ b/src/index.js @@ -114,6 +114,8 @@ .append('
Thank you for your responses.

') .append('') } + + updateProgress(questions)(responses) } const setupQuestions = questions => responses => { @@ -159,7 +161,6 @@ } else { // Count valid responses updateQuizViewStatus(questions)(responses) - updateProgress(questions)(responses) setQuiz({responses}) } } @@ -171,6 +172,5 @@ setupQuizElement(data)(document.getElementById('quiz')) setupQuestions(questions)(responses) updateQuizViewStatus(questions)(responses) - updateProgress(questions)(responses) }) })($, JSON, localStorage) From 38c7c3db46c01f58c9f06b3cb6872da75b00fb28 Mon Sep 17 00:00:00 2001 From: David Almeida Date: Tue, 28 Nov 2017 21:03:55 +0100 Subject: [PATCH 24/26] improve performance mounting questions on demand --- src/index.js | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/index.js b/src/index.js index 56acd3c..8dc1a71 100644 --- a/src/index.js +++ b/src/index.js @@ -90,6 +90,7 @@ ).get(0) } const appendToQuizForm = element => $('#quiz-form').append(element) + const removeFromQuizForm = element => $(element).remove() const updateProgress = questions => responses => { const responseCount = countValidResponses(responses) @@ -97,15 +98,18 @@ .css('width', (responseCount / questions.length * 100) + '%') } + const getQuestionId = id => `question_${id}` + const updateQuizViewStatus = questions => responses => { const current = countValidResponses(responses) + const question = questions[current] + question.input = question.input || { input: {type:'input'} } + + const nextQuestion = createQuestionElement(question)(getQuestionId(current))(responses[current]) + nextQuestion && appendToQuizForm(nextQuestion) - $('#quiz-form') - .find(`.card[id^=question_]`) - .css('display', 'none') - $('#quiz-form') - .find(`#question_${current}`) - .css('display', 'block') + const previousQuestion = document.getElementById(getQuestionId(current-1)) + previousQuestion && removeFromQuizForm(previousQuestion) // Is case all questions have been responded if (questions.length <= current ) { @@ -118,14 +122,6 @@ updateProgress(questions)(responses) } - const setupQuestions = questions => responses => { - const defaultInput = { input: {type:'input'} } - questions - .map(question => question.input ? question : Object.assign({}, defaultInput, question)) - .map((question, i) => createQuestionElement(question)(`question_${i}`)(responses[i])) - .map(appendToQuizForm) - } - const resetQuiz = () => { localStorage.removeItem('quiz') location.reload(); @@ -147,7 +143,7 @@ let {responses=[]} = getQuiz() const currentQuestion = responses.length const getFormInputs = getInputsByName(document.getElementById('quiz-form')) - const inputs = getFormInputs(`question_${currentQuestion}`) + const inputs = getFormInputs(getQuestionId(currentQuestion)) const response = inputs .filter(({checked, type}) => type === 'text' || checked ) // has checked and it's false .map(({value}) => value) // map to actual values @@ -170,7 +166,6 @@ let {responses=[]} = getQuiz() setupQuizElement(data)(document.getElementById('quiz')) - setupQuestions(questions)(responses) updateQuizViewStatus(questions)(responses) }) })($, JSON, localStorage) From 18746ab578e833eac2956b5216b40d8ac0a9ac18 Mon Sep 17 00:00:00 2001 From: David Almeida Date: Mon, 11 Dec 2017 16:48:48 +0100 Subject: [PATCH 25/26] Fix error on final response --- src/index.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/index.js b/src/index.js index 8dc1a71..156416b 100644 --- a/src/index.js +++ b/src/index.js @@ -1,8 +1,9 @@ (function($, JSON, localStorage){ console.clear() - const {url} = options = { + const options = { url: `data/quiz.json?${Date.now()}` } + const {url} = options const getOptionsMarkup = type => options => id => response => { return '
' @@ -102,13 +103,8 @@ const updateQuizViewStatus = questions => responses => { const current = countValidResponses(responses) - const question = questions[current] - question.input = question.input || { input: {type:'input'} } - - const nextQuestion = createQuestionElement(question)(getQuestionId(current))(responses[current]) - nextQuestion && appendToQuizForm(nextQuestion) - const previousQuestion = document.getElementById(getQuestionId(current-1)) + previousQuestion && removeFromQuizForm(previousQuestion) // Is case all questions have been responded @@ -117,6 +113,12 @@ $('#quiz') .append('
Thank you for your responses.

') .append('') + } else { + const question = questions[current] + question.input = question.input || { input: {type:'input'} } + + const nextQuestion = createQuestionElement(question)(getQuestionId(current))(responses[current]) + nextQuestion && appendToQuizForm(nextQuestion) } updateProgress(questions)(responses) From 77a7a4f2c9cfd5aae481d4a4700176aa74f324cd Mon Sep 17 00:00:00 2001 From: David Almeida Date: Tue, 12 Dec 2017 15:13:17 +0100 Subject: [PATCH 26/26] adapt tests to performance improvement --- test/helpers/index.js | 2 +- test/quiz.spec.js | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/helpers/index.js b/test/helpers/index.js index bf48672..70f9328 100644 --- a/test/helpers/index.js +++ b/test/helpers/index.js @@ -3,7 +3,7 @@ const BUTTON_SELECTOR = '.primary.button' const RESET_SELECTOR = '.negative.button' const PROGRESS_SELECTOR = '#progress' const RESPONSE_MSG = 'You must give a response' -const questionSelector = num => `.card:nth-child(${num})` +const questionSelector = num => `#question_${num-1}` module.exports = { data, diff --git a/test/quiz.spec.js b/test/quiz.spec.js index bc16d06..7bdf03e 100644 --- a/test/quiz.spec.js +++ b/test/quiz.spec.js @@ -40,9 +40,9 @@ describe('Quiz', () => { client.expect.element(questionSelector(1)) .to.be.visible client.expect.element(questionSelector(2)) - .to.not.be.visible + .to.not.be.present client.expect.element(questionSelector(3)) - .to.not.be.visible + .to.not.be.present }) it('should display description of the question', (client) => { @@ -81,7 +81,7 @@ describe('Quiz', () => { it('should display the next question', (client) => { client.expect.element(questionSelector(1)) - .to.not.be.visible + .to.not.be.present client.expect.element(questionSelector(2)) .to.be.visible }) @@ -102,7 +102,7 @@ describe('Quiz', () => { it('should display the last non answered question', (client) => { client.expect.element(questionSelector(1)) - .to.not.be.visible + .to.not.be.present client.expect.element(questionSelector(2)) .to.be.visible }) @@ -120,7 +120,7 @@ describe('Quiz', () => { client.expect.element(questionSelector(1)) .to.be.visible client.expect.element(questionSelector(2)) - .to.not.be.visible + .to.not.be.present }) }) })