From cb04c212f9c11393f417769a7f38f6982185fa6b Mon Sep 17 00:00:00 2001 From: Lily2point0 Date: Tue, 30 May 2017 16:29:42 +0100 Subject: [PATCH 01/13] added shift+tab commands --- src/js/oCrossword.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/js/oCrossword.js b/src/js/oCrossword.js index ce0e872..3347caa 100644 --- a/src/js/oCrossword.js +++ b/src/js/oCrossword.js @@ -390,6 +390,10 @@ OCrossword.prototype.assemble = function assemble() { e.preventDefault(); } + if(e.shiftKey && e.keyCode === 9) { + return progress(-1); + } + if (e.keyCode === 13) { //enter magicInputNextEls = null; return progress(); @@ -446,11 +450,15 @@ OCrossword.prototype.assemble = function assemble() { let gridSync = getCellFromClue(e.target); + if(e.shiftKey && e.keyCode === 9) { + return nextInput(e.target, -1); + } if (e.keyCode === 13) { //enter e.target.blur(); return; } + if ( e.keyCode === 9 || //tab e.keyCode === 40 ||//down From 5bd79ed44cef662135c65a9551f61a93d3288cb0 Mon Sep 17 00:00:00 2001 From: Lily2point0 Date: Tue, 30 May 2017 17:08:59 +0100 Subject: [PATCH 02/13] restricted input to letters --- src/js/oCrossword.js | 61 ++++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/src/js/oCrossword.js b/src/js/oCrossword.js index 3347caa..8d06f6d 100644 --- a/src/js/oCrossword.js +++ b/src/js/oCrossword.js @@ -375,6 +375,7 @@ OCrossword.prototype.assemble = function assemble() { wrapper.appendChild(cluesEl); const magicInput = document.createElement('input'); + magicInput.setAttribute('pattern', '[a-zA-Z]'); gridScaleWrapper.appendChild(magicInput); magicInput.classList.add('o-crossword-magic-input'); let magicInputTargetEl = null; @@ -398,6 +399,7 @@ OCrossword.prototype.assemble = function assemble() { magicInputNextEls = null; return progress(); } + if ( e.keyCode === 9 || //tab e.keyCode === 40 ||//down @@ -412,6 +414,7 @@ OCrossword.prototype.assemble = function assemble() { ) { return progress(-1); } + if ( e.keyCode === 8 //backspace ) { @@ -427,17 +430,21 @@ OCrossword.prototype.assemble = function assemble() { return; } - if(!isAndroid()) { - magicInput.value = String.fromCharCode(e.keyCode); + if(e.keyCode >= 65 && e.keyCode <= 90) { + if(!isAndroid()) { + magicInput.value = String.fromCharCode(e.keyCode); - if( e.keyCode === 229) { - //fix safari press down - magicInput.value = ''; - return; + if( e.keyCode === 229) { + //fix safari press down + magicInput.value = ''; + return; + } } + + progress(); + } else { + return; } - - progress(); }); this.addEventListener(cluesEl, 'keydown', function(e){ @@ -458,7 +465,7 @@ OCrossword.prototype.assemble = function assemble() { e.target.blur(); return; } - + if ( e.keyCode === 9 || //tab e.keyCode === 40 ||//down @@ -499,26 +506,30 @@ OCrossword.prototype.assemble = function assemble() { return; } - if(!isAndroid()) { - e.target.value = String.fromCharCode(e.keyCode); + if(e.keyCode >= 65 && e.keyCode <= 90) { + if(!isAndroid()) { + e.target.value = String.fromCharCode(e.keyCode); - if( e.keyCode === 229) { - //fix safari press down - e.target.value = ''; - return; + if( e.keyCode === 229) { + //fix safari press down + e.target.value = ''; + return; + } } - } - setTimeout(function(){ - gridSync.grid.textContent = e.target.value; - - if(gridSync.defSync) { - let defSync = cluesEl.querySelector('input[data-link-identifier="' + gridSync.defSyncInput +'"]'); - defSync.value = e.target.value; - } + setTimeout(function(){ + gridSync.grid.textContent = e.target.value; + + if(gridSync.defSync) { + let defSync = cluesEl.querySelector('input[data-link-identifier="' + gridSync.defSyncInput +'"]'); + defSync.value = e.target.value; + } - nextInput(e.target, 1); - }, timer); + nextInput(e.target, 1); + }, timer); + } else { + return; + } }); function nextInput(source, direction) { From 7eacdaacd57c139bf5baf68caac9be9567968730 Mon Sep 17 00:00:00 2001 From: Lily2point0 Date: Tue, 30 May 2017 17:10:46 +0100 Subject: [PATCH 03/13] prevent control keys from clearing input --- src/js/oCrossword.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/js/oCrossword.js b/src/js/oCrossword.js index 8d06f6d..0f54cb7 100644 --- a/src/js/oCrossword.js +++ b/src/js/oCrossword.js @@ -426,7 +426,6 @@ OCrossword.prototype.assemble = function assemble() { e.keyCode === 20 || //caps lock e.keyCode === 91 //Command ) { - magicInput.value = ''; return; } @@ -502,7 +501,6 @@ OCrossword.prototype.assemble = function assemble() { e.keyCode === 20 || //caps lock e.keyCode === 91 //Command ) { - e.target.value = ''; return; } From 5aa7ef5d03cf9b95fd0cd12abc365fe5759b2c8f Mon Sep 17 00:00:00 2001 From: Lily2point0 Date: Tue, 30 May 2017 17:16:31 +0100 Subject: [PATCH 04/13] remove Safari fix as it doesn't meet wrapping condition --- src/js/oCrossword.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/js/oCrossword.js b/src/js/oCrossword.js index 0f54cb7..33d3d23 100644 --- a/src/js/oCrossword.js +++ b/src/js/oCrossword.js @@ -432,12 +432,6 @@ OCrossword.prototype.assemble = function assemble() { if(e.keyCode >= 65 && e.keyCode <= 90) { if(!isAndroid()) { magicInput.value = String.fromCharCode(e.keyCode); - - if( e.keyCode === 229) { - //fix safari press down - magicInput.value = ''; - return; - } } progress(); @@ -507,12 +501,6 @@ OCrossword.prototype.assemble = function assemble() { if(e.keyCode >= 65 && e.keyCode <= 90) { if(!isAndroid()) { e.target.value = String.fromCharCode(e.keyCode); - - if( e.keyCode === 229) { - //fix safari press down - e.target.value = ''; - return; - } } setTimeout(function(){ From e9736c02c277577641e37bf81b52a267ba285766 Mon Sep 17 00:00:00 2001 From: Lily2point0 Date: Wed, 31 May 2017 11:30:18 +0100 Subject: [PATCH 05/13] added accessibility labels to navigate clue list --- src/js/oCrossword.js | 45 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/src/js/oCrossword.js b/src/js/oCrossword.js index 33d3d23..a29b51e 100644 --- a/src/js/oCrossword.js +++ b/src/js/oCrossword.js @@ -101,7 +101,9 @@ function buildGrid( const tempLi = document.createElement('li'); const tempSpan = document.createElement('span'); const tempPartial = document.createElement('div'); + tempLi.setAttribute('tabindex', '0'); tempPartial.classList.add('o-crossword-user-answer'); + // tempPartial.classList.add('inactive'); const answerLength = across[2].filter(isFinite).filter(isFinite).reduce((a,b)=>a+b,0); tempSpan.textContent = across[0] + '. ' + across[1]; @@ -114,6 +116,7 @@ function buildGrid( let tempInput = document.createElement('input'); tempInput.setAttribute('maxlength', 1); tempInput.setAttribute('data-link-identifier', 'A' + across[0] + '-' + i); + // tempInput.setAttribute('tabindex', -1); if(answers) { tempInput.value = answers.across[index][i]; } @@ -152,6 +155,7 @@ function buildGrid( const tempLi = document.createElement('li'); const tempSpan = document.createElement('span'); const tempPartial = document.createElement('div'); + tempLi.setAttribute('tabindex', '0'); tempPartial.classList.add('o-crossword-user-answer'); const answerLength = down[2].filter(isFinite).filter(isFinite).reduce((a,b)=>a+b,0); @@ -165,6 +169,7 @@ function buildGrid( let tempInput = document.createElement('input'); tempInput.setAttribute('maxlength', 1); tempInput.setAttribute('data-link-identifier', 'D' + down[0] + '-' + i); + tempInput.setAttribute('tabindex', -1); if(answers) { tempInput.value = answers.down[index][i]; @@ -332,6 +337,7 @@ OCrossword.prototype.assemble = function assemble() { if (cluesEl) { let currentClue = -1; + const cluesTotal = parseInt(this.rootEl.parentElement.getAttribute('data-o-crossword-clue-length')) - 1; const cluesUlEls = Array.from(cluesEl.querySelectorAll('ul')); @@ -375,7 +381,6 @@ OCrossword.prototype.assemble = function assemble() { wrapper.appendChild(cluesEl); const magicInput = document.createElement('input'); - magicInput.setAttribute('pattern', '[a-zA-Z]'); gridScaleWrapper.appendChild(magicInput); magicInput.classList.add('o-crossword-magic-input'); let magicInputTargetEl = null; @@ -448,6 +453,39 @@ OCrossword.prototype.assemble = function assemble() { timer = 0; } + //TODO: a11y tests + if(e.target.nodeName !== 'INPUT') { + if(e.keyCode === 9) { + // e.target.parentNode.getAttribute + if(e.shiftKey) { + --currentClue; + if(currentClue < 0) { + currentClue = cluesTotal; + } + } else { + ++currentClue; + if(currentClue > cluesTotal) { + currentClue = 0; + } + } + + let nextFocus = cluesEl.querySelector('li[data-o-crossword-clue-id="'+ currentClue +'"]'); + nextFocus.focus(); + } + + if(e.keyCode === 13) { + let inputs = e.target.querySelectorAll('input'); + Array.from(inputs).forEach(input => { + input.setAttribute('tabindex', 1); + }); + + inputs[0].focus(); + } + + return; + } + //end TODO + let gridSync = getCellFromClue(e.target); if(e.shiftKey && e.keyCode === 9) { @@ -531,6 +569,10 @@ OCrossword.prototype.assemble = function assemble() { } else { source.blur(); let def = source.parentElement.parentElement; + let inputs = cluesEl.querySelectorAll('input'); + Array.from(inputs).forEach(input => { + input.setAttribute('tabindex', -1); + }); def.click(); } } @@ -941,7 +983,6 @@ OCrossword.prototype.assemble = function assemble() { const navigateClues = function navigateClues (e) { e.preventDefault(); const dir = (e.target === clueNavigationNext)?'forward':'backward'; - const cluesTotal = parseInt(this.rootEl.parentElement.getAttribute('data-o-crossword-clue-length')) - 1; if (dir === 'forward') { ++currentClue; From 8af93033273a5119e8aa82874524bdab4c00e05b Mon Sep 17 00:00:00 2001 From: Lily2point0 Date: Wed, 31 May 2017 11:46:33 +0100 Subject: [PATCH 06/13] added down & across labels for screen readers --- src/js/oCrossword.js | 10 +++++----- src/scss/_base.scss | 8 ++++++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/js/oCrossword.js b/src/js/oCrossword.js index a29b51e..4e3eeb3 100644 --- a/src/js/oCrossword.js +++ b/src/js/oCrossword.js @@ -103,10 +103,10 @@ function buildGrid( const tempPartial = document.createElement('div'); tempLi.setAttribute('tabindex', '0'); tempPartial.classList.add('o-crossword-user-answer'); - // tempPartial.classList.add('inactive'); const answerLength = across[2].filter(isFinite).filter(isFinite).reduce((a,b)=>a+b,0); - tempSpan.textContent = across[0] + '. ' + across[1]; + tempSpan.innerHTML = across[0] + 'across' + '. ' + across[1]; + console.log(tempSpan); tempLi.dataset.oCrosswordNumber = across[0]; tempLi.dataset.oCrosswordAnswerLength = answerLength; tempLi.dataset.oCrosswordDirection = 'across'; @@ -159,7 +159,7 @@ function buildGrid( tempPartial.classList.add('o-crossword-user-answer'); const answerLength = down[2].filter(isFinite).filter(isFinite).reduce((a,b)=>a+b,0); - tempSpan.textContent = down[0] + '. ' + down[1]; + tempSpan.innerHTML = down[0] + 'down' + '. ' + down[1]; tempLi.dataset.oCrosswordNumber = down[0]; tempLi.dataset.oCrosswordAnswerLength = answerLength; tempLi.dataset.oCrosswordDirection = 'down'; @@ -801,7 +801,7 @@ OCrossword.prototype.assemble = function assemble() { function setClue(number, direction) { const el = cluesEl.querySelector(`li[data-o-crossword-number="${number}"][data-o-crossword-direction="${direction}"]`); if (el) { - clueDisplayerText.textContent = el.querySelector('span').textContent; + clueDisplayerText.innerHTML = el.querySelector('span').innerHTML; const els = Array.from(cluesEl.getElementsByClassName('has-hover')); els.filter(el2 => el2 !== el).forEach(el => el.classList.remove('has-hover')); el.classList.add('has-hover'); @@ -829,7 +829,7 @@ OCrossword.prototype.assemble = function assemble() { } if (el) { - clueDisplayerText.textContent = ''; + clueDisplayerText.innerHTML = ''; const els = Array.from(cluesEl.getElementsByClassName('has-hover')); els.forEach(el => el.classList.remove('has-hover')); el.classList.remove('has-hover'); diff --git a/src/scss/_base.scss b/src/scss/_base.scss index 241637c..02e11b9 100644 --- a/src/scss/_base.scss +++ b/src/scss/_base.scss @@ -89,6 +89,10 @@ vertical-align: middle; padding: 0; width: 100%; + + .sr-direction { + display: none; + } } } @@ -233,6 +237,10 @@ .has-hover > span{ @include oColorsFor(o-crossword-clue-highlight, text); } + + .sr-direction { + display: none; + } } } From d409c0e566be1e04f47d95a60baa895848db5ade Mon Sep 17 00:00:00 2001 From: Lily2point0 Date: Wed, 31 May 2017 13:58:49 +0100 Subject: [PATCH 07/13] make screen reader read inputs properly --- src/js/oCrossword.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/js/oCrossword.js b/src/js/oCrossword.js index 4e3eeb3..7edc56b 100644 --- a/src/js/oCrossword.js +++ b/src/js/oCrossword.js @@ -71,6 +71,7 @@ function buildGrid( td.classList.add('empty'); const emptyMarker = emptyCell.cloneNode(true); emptyMarker.classList.remove('hidden'); + emptyMarker.setAttribute('aria-hidden', true); td.appendChild(emptyMarker); } } @@ -106,7 +107,6 @@ function buildGrid( const answerLength = across[2].filter(isFinite).filter(isFinite).reduce((a,b)=>a+b,0); tempSpan.innerHTML = across[0] + 'across' + '. ' + across[1]; - console.log(tempSpan); tempLi.dataset.oCrosswordNumber = across[0]; tempLi.dataset.oCrosswordAnswerLength = answerLength; tempLi.dataset.oCrosswordDirection = 'across'; @@ -359,7 +359,6 @@ OCrossword.prototype.assemble = function assemble() { const clueNavigation = document.createElement('nav'); clueNavigation.classList.add('o-crossword-clue-navigation'); - // clueNavigation.classList.add('hidden'); const clueNavigationPrev = document.createElement('a'); clueNavigationPrev.classList.add('o-crossword-clue-nav-prev'); @@ -450,13 +449,12 @@ OCrossword.prototype.assemble = function assemble() { if (!isAndroid()) { e.preventDefault(); - timer = 0; + timer = 100; } //TODO: a11y tests if(e.target.nodeName !== 'INPUT') { if(e.keyCode === 9) { - // e.target.parentNode.getAttribute if(e.shiftKey) { --currentClue; if(currentClue < 0) { @@ -539,6 +537,7 @@ OCrossword.prototype.assemble = function assemble() { if(e.keyCode >= 65 && e.keyCode <= 90) { if(!isAndroid()) { e.target.value = String.fromCharCode(e.keyCode); + e.target.select(); //a11y: screen reader reads value properly } setTimeout(function(){ From 6523394c0a7c38794399095e9274b0a52e8687f2 Mon Sep 17 00:00:00 2001 From: Lily2point0 Date: Wed, 31 May 2017 15:50:37 +0100 Subject: [PATCH 08/13] fix screen reader for grid input --- demos/src/basic.mustache | 50 +++++++++++++++++++++++++++++++++++++++- src/js/oCrossword.js | 18 +++++++++------ 2 files changed, 60 insertions(+), 8 deletions(-) diff --git a/demos/src/basic.mustache b/demos/src/basic.mustache index 8819dd6..35a858f 100644 --- a/demos/src/basic.mustache +++ b/demos/src/basic.mustache @@ -1,5 +1,53 @@
-
+
    diff --git a/src/js/oCrossword.js b/src/js/oCrossword.js index 7edc56b..5f75ddf 100644 --- a/src/js/oCrossword.js +++ b/src/js/oCrossword.js @@ -102,7 +102,7 @@ function buildGrid( const tempLi = document.createElement('li'); const tempSpan = document.createElement('span'); const tempPartial = document.createElement('div'); - tempLi.setAttribute('tabindex', '0'); + tempLi.setAttribute('tabindex', 0); tempPartial.classList.add('o-crossword-user-answer'); const answerLength = across[2].filter(isFinite).filter(isFinite).reduce((a,b)=>a+b,0); @@ -116,7 +116,7 @@ function buildGrid( let tempInput = document.createElement('input'); tempInput.setAttribute('maxlength', 1); tempInput.setAttribute('data-link-identifier', 'A' + across[0] + '-' + i); - // tempInput.setAttribute('tabindex', -1); + tempInput.setAttribute('tabindex', -1); if(answers) { tempInput.value = answers.across[index][i]; } @@ -155,7 +155,7 @@ function buildGrid( const tempLi = document.createElement('li'); const tempSpan = document.createElement('span'); const tempPartial = document.createElement('div'); - tempLi.setAttribute('tabindex', '0'); + tempLi.setAttribute('tabindex', 0); tempPartial.classList.add('o-crossword-user-answer'); const answerLength = down[2].filter(isFinite).filter(isFinite).reduce((a,b)=>a+b,0); @@ -436,8 +436,15 @@ OCrossword.prototype.assemble = function assemble() { if(e.keyCode >= 65 && e.keyCode <= 90) { if(!isAndroid()) { magicInput.value = String.fromCharCode(e.keyCode); + + let last = gridMap.get(magicInputTargetEl); + Array.from(last).forEach(cell => { + if(parseInt(cell.answerLength) - cell.answerPos === 1) { + e.target.select(); + } + }); //a11y fix for screen reader } - + progress(); } else { return; @@ -452,7 +459,6 @@ OCrossword.prototype.assemble = function assemble() { timer = 100; } - //TODO: a11y tests if(e.target.nodeName !== 'INPUT') { if(e.keyCode === 9) { if(e.shiftKey) { @@ -482,7 +488,6 @@ OCrossword.prototype.assemble = function assemble() { return; } - //end TODO let gridSync = getCellFromClue(e.target); @@ -703,7 +708,6 @@ OCrossword.prototype.assemble = function assemble() { setTimeout(function(){ magicInput.focus(); - magicInput.select(); }, timer); } From ca7a85e9919715b858fa977d9d6eb3f76e2563e3 Mon Sep 17 00:00:00 2001 From: Lily2point0 Date: Thu, 1 Jun 2017 11:36:01 +0100 Subject: [PATCH 09/13] added answer readout for screen readers + refactor code with logical function ordering --- src/js/oCrossword.js | 377 +++++++++++++++++++++++-------------------- src/scss/_base.scss | 8 +- 2 files changed, 212 insertions(+), 173 deletions(-) diff --git a/src/js/oCrossword.js b/src/js/oCrossword.js index 5f75ddf..451c363 100644 --- a/src/js/oCrossword.js +++ b/src/js/oCrossword.js @@ -106,7 +106,7 @@ function buildGrid( tempPartial.classList.add('o-crossword-user-answer'); const answerLength = across[2].filter(isFinite).filter(isFinite).reduce((a,b)=>a+b,0); - tempSpan.innerHTML = across[0] + 'across' + '. ' + across[1]; + tempSpan.innerHTML = across[0] + 'across' + '. ' + across[1] + ' Press ENTER to add your answer'; tempLi.dataset.oCrosswordNumber = across[0]; tempLi.dataset.oCrosswordAnswerLength = answerLength; tempLi.dataset.oCrosswordDirection = 'across'; @@ -159,7 +159,7 @@ function buildGrid( tempPartial.classList.add('o-crossword-user-answer'); const answerLength = down[2].filter(isFinite).filter(isFinite).reduce((a,b)=>a+b,0); - tempSpan.innerHTML = down[0] + 'down' + '. ' + down[1]; + tempSpan.innerHTML = down[0] + 'down' + '. ' + down[1] + ' Press ENTER to add your answer'; tempLi.dataset.oCrosswordNumber = down[0]; tempLi.dataset.oCrosswordAnswerLength = answerLength; tempLi.dataset.oCrosswordDirection = 'down'; @@ -390,6 +390,22 @@ OCrossword.prototype.assemble = function assemble() { let blockHighlight = false; let previousClueSelection = null; + function constructInputIdentifier(data, direction) { + let identifier; + + for(let i = 0; i < data.length; ++i) { + if(data[i].direction !== direction) { + identifier = data[i].direction.slice(0,1).toUpperCase(); + identifier += data[i].number; + identifier += '-'; + identifier += data[i].answerPos; + } + } + + return identifier; + } + + this.addEventListener(magicInput, 'keydown', function (e) { if (!isAndroid()) { e.preventDefault(); @@ -526,6 +542,8 @@ OCrossword.prototype.assemble = function assemble() { defSync.value = e.target.value; } + updateScreenReaderAnswer(e.target); + nextInput(e.target, -1); }, timer); @@ -553,6 +571,8 @@ OCrossword.prototype.assemble = function assemble() { defSync.value = e.target.value; } + updateScreenReaderAnswer(e.target, gridSync); + nextInput(e.target, 1); }, timer); } else { @@ -560,72 +580,6 @@ OCrossword.prototype.assemble = function assemble() { } }); - function nextInput(source, direction) { - let inputID = source.getAttribute('data-link-identifier'); - let inputGroup = document.querySelectorAll('input[data-link-identifier^="' + inputID.split('-')[0] +'-"]'); - let currentInput = inputID.split('-')[1]; - let newInput = (direction === 1)?++currentInput:--currentInput; - - if(newInput >= 0 && newInput < inputGroup.length) { - let next = cluesEl.querySelector('input[data-link-identifier="' + inputID.split('-')[0] +'-'+ newInput+'"]'); - next.focus(); - next.select(); - } else { - source.blur(); - let def = source.parentElement.parentElement; - let inputs = cluesEl.querySelectorAll('input'); - Array.from(inputs).forEach(input => { - input.setAttribute('tabindex', -1); - }); - def.click(); - } - } - - function getCellFromClue(clue) { - let inputIdentifier = clue.getAttribute('data-link-identifier'); - let defDirection = (inputIdentifier.slice(0,1) === 'A')?'across':'down'; - let defNum = inputIdentifier.slice(1,inputIdentifier.length).split('-')[0]; - let defIndex = parseInt(inputIdentifier.split('-')[1]); - - let cells = gridEl.querySelectorAll('td:not(.empty)'); - let selectedCell = {}; - - Array.from(cells).forEach(cell => { - let cellData = gridMap.get(cell); - for(let i = 0; i < cellData.length; ++i) { - if( - cellData[i].direction === defDirection && - parseInt(cellData[i].number) === parseInt(defNum) && - parseInt(cellData[i].answerPos) === parseInt(defIndex) - ) { - selectedCell.grid = cell; - if(cellData.length > 1) { - selectedCell.defSync = true; - - selectedCell.defSyncInput = constructInputIdentifier(cellData, defDirection); - } - } - } - }); - - return selectedCell; - } - - function constructInputIdentifier(data, direction) { - let identifier; - - for(let i = 0; i < data.length; ++i) { - if(data[i].direction !== direction) { - identifier = data[i].direction.slice(0,1).toUpperCase(); - identifier += data[i].number; - identifier += '-'; - identifier += data[i].answerPos; - } - } - - return identifier; - } - const progress = debounce(function progress(direction) { direction = direction === -1 ? -1 : 1; const oldMagicInputEl = magicInputTargetEl; @@ -711,78 +665,25 @@ OCrossword.prototype.assemble = function assemble() { }, timer); } - const onResize = function onResize(init) { - var isMobile = false; - const cellSizeMax = 40; - - if (window.innerWidth <= 739) { - isMobile = true; - } else if (window.innerWidth > window.innerHeight && window.innerHeight <=739 ) { //rotated phones and small devices, but not iOS - isMobile = true; - } - - if(isMobile && !!init) { - clueNavigationNext.click(); - } - - const d1 = cluesEl.getBoundingClientRect(); - let d2 = gridEl.getBoundingClientRect(); - const width1 = d1.width; - const height1 = d1.height; - let width2 = d2.width; - const height2 = d2.height; - - let scale = height2/height1; - if (scale > 0.2) scale = 0.2; - - this._cluesElHeight = height1; - this._cluesElWidth = width1 * scale; - this._height = height1 * scale; - this._scale = scale; - - magicInput.style.display = 'none'; - - //update grid size to fill 100% on mobile view - const fullWidth = Math.min(window.innerHeight, window.innerWidth); - this.rootEl.width = fullWidth + 'px !important'; - const gridTDs = gridEl.querySelectorAll('td'); - const gridSize = gridEl.querySelectorAll('tr').length; - const newTdWidth = parseInt(fullWidth / (gridSize + 1) ); - const inputEl = document.querySelector('.o-crossword-magic-input'); - - if(isMobile) { - for (let i = 0; i < gridTDs.length; i++) { - let td = gridTDs[i]; - td.style.width = Math.min(newTdWidth, cellSizeMax) + "px"; - td.style.height = Math.min(newTdWidth, cellSizeMax) + "px"; - td.style.maxWidth = "initial"; - td.style.minWidth = "initial"; - } + function nextInput(source, direction) { + let inputID = source.getAttribute('data-link-identifier'); + let inputGroup = document.querySelectorAll('input[data-link-identifier^="' + inputID.split('-')[0] +'-"]'); + let currentInput = inputID.split('-')[1]; + let newInput = (direction === 1)?++currentInput:--currentInput; - inputEl.style.width = Math.min(newTdWidth, cellSizeMax) + "px"; - inputEl.style.height = Math.min(newTdWidth, cellSizeMax) + "px"; - inputEl.style.maxWidth = "initial"; + if(newInput >= 0 && newInput < inputGroup.length) { + let next = cluesEl.querySelector('input[data-link-identifier="' + inputID.split('-')[0] +'-'+ newInput+'"]'); + next.focus(); + next.select(); } else { - for (let i = 0; i < gridTDs.length; i++) { - let td = gridTDs[i]; - td.style.removeProperty('width'); - td.style.removeProperty('height'); - td.style.removeProperty('max-width'); - td.style.removeProperty('min-width'); - } - - let desktopSize = gridTDs[0].getBoundingClientRect().width; - inputEl.style.width = desktopSize + "px"; - inputEl.style.height = desktopSize + "px"; + source.blur(); + let def = source.parentElement.parentElement; + let inputs = cluesEl.querySelectorAll('input'); + Array.from(inputs).forEach(input => { + input.setAttribute('tabindex', -1); + }); + def.click(); } - - d2 = gridEl.getBoundingClientRect(); - clueDisplayer.style.width = d2.width + 'px'; - - }.bind(this); - - if(!isAndroid()) { - this.onResize = debounce(onResize, 100); } function highlightGridByCluesEl(el) { @@ -801,17 +702,6 @@ OCrossword.prototype.assemble = function assemble() { return false; } - function setClue(number, direction) { - const el = cluesEl.querySelector(`li[data-o-crossword-number="${number}"][data-o-crossword-direction="${direction}"]`); - if (el) { - clueDisplayerText.innerHTML = el.querySelector('span').innerHTML; - const els = Array.from(cluesEl.getElementsByClassName('has-hover')); - els.filter(el2 => el2 !== el).forEach(el => el.classList.remove('has-hover')); - el.classList.add('has-hover'); - currentClue = parseInt(el.getAttribute('data-o-crossword-clue-id')); - } - } - function highlightGridByNumber(number, direction, length) { magicInput.style.display = 'none'; setClue(number, direction); @@ -823,6 +713,47 @@ OCrossword.prototype.assemble = function assemble() { gridElsToHighlight.forEach(el => el.dataset.oCrosswordHighlighted = direction); } + function getCellFromClue(clue) { + let inputIdentifier = clue.getAttribute('data-link-identifier'); + let defDirection = (inputIdentifier.slice(0,1) === 'A')?'across':'down'; + let defNum = inputIdentifier.slice(1,inputIdentifier.length).split('-')[0]; + let defIndex = parseInt(inputIdentifier.split('-')[1]); + + let cells = gridEl.querySelectorAll('td:not(.empty)'); + let selectedCell = {}; + + Array.from(cells).forEach(cell => { + let cellData = gridMap.get(cell); + for(let i = 0; i < cellData.length; ++i) { + if( + cellData[i].direction === defDirection && + parseInt(cellData[i].number) === parseInt(defNum) && + parseInt(cellData[i].answerPos) === parseInt(defIndex) + ) { + selectedCell.grid = cell; + if(cellData.length > 1) { + selectedCell.defSync = true; + + selectedCell.defSyncInput = constructInputIdentifier(cellData, defDirection); + } + } + } + }); + + return selectedCell; + } + + function setClue(number, direction) { + const el = cluesEl.querySelector(`li[data-o-crossword-number="${number}"][data-o-crossword-direction="${direction}"]`); + if (el) { + clueDisplayerText.innerHTML = el.querySelector('span').innerHTML; + const els = Array.from(cluesEl.getElementsByClassName('has-hover')); + els.filter(el2 => el2 !== el).forEach(el => el.classList.remove('has-hover')); + el.classList.add('has-hover'); + currentClue = parseInt(el.getAttribute('data-o-crossword-clue-id')); + } + } + function unsetClue(number, direction) { const el = cluesEl.querySelector(`li[data-o-crossword-number="${number}"][data-o-crossword-direction="${direction}"]`); const els = Array.from(gridEl.querySelectorAll('td[data-o-crossword-highlighted]')); @@ -842,6 +773,49 @@ OCrossword.prototype.assemble = function assemble() { magicInput.style.display = 'none'; } + function toggleClueSelection(clue) { + if (previousClueSelection !== null && isEquivalent(previousClueSelection, clue)) { + unsetClue(clue.number, clue.direction); + blockHighlight = false; + previousClueSelection = null; + return false; + } + + blockHighlight = true; + previousClueSelection = clue; + + return true; + } + + function updateScreenReaderAnswer(target, dataGrid) { + const targetData = target.parentNode.parentNode; + const answerLength = parseInt(targetData.getAttribute('data-o-crossword-answer-length')); + const inputs = targetData.querySelectorAll('input'); + const screenReaderAnswer = targetData.querySelector('.sr-answer'); + let answerValue = []; + let filledCount = 0; + + Array.from(inputs).forEach(input => { + if(input.value !== '') { + ++filledCount; + answerValue.push(input.value); + } else { + answerValue.push(" blank "); + } + }); + + if(filledCount > 0) { + screenReaderAnswer.textContent = 'Your Answer: ' + answerValue.join('') + '.'; + } else { + screenReaderAnswer.textContent = ''; + } + + if(dataGrid && dataGrid.defSync) { + let syncTarget = cluesEl.querySelector('input[data-link-identifier=' + dataGrid.defSyncInput + ']'); + updateScreenReaderAnswer(syncTarget); + } + } + function syncPartialClue(letter, src, index) { const gridItems = gridMap.get(src[index]); let targets = []; @@ -853,39 +827,82 @@ OCrossword.prototype.assemble = function assemble() { Array.from(targets).forEach((target) => { target.value = letter; + updateScreenReaderAnswer(target); }); } - function isEquivalent(a, b) { - var aProps = Object.getOwnPropertyNames(a); - var bProps = Object.getOwnPropertyNames(b); + const onResize = function onResize(init) { + var isMobile = false; + const cellSizeMax = 40; + + if (window.innerWidth <= 739) { + isMobile = true; + } else if (window.innerWidth > window.innerHeight && window.innerHeight <=739 ) { //rotated phones and small devices, but not iOS + isMobile = true; + } - if (aProps.length != bProps.length) { - return false; - } + if(isMobile && !!init) { + clueNavigationNext.click(); + } - for (var i = 0; i < aProps.length; i++) { - var propName = aProps[i]; - if (a[propName] !== b[propName]) { - return false; - } - } + const d1 = cluesEl.getBoundingClientRect(); + let d2 = gridEl.getBoundingClientRect(); + const width1 = d1.width; + const height1 = d1.height; + let width2 = d2.width; + const height2 = d2.height; - return true; - } + let scale = height2/height1; + if (scale > 0.2) scale = 0.2; - function toggleClueSelection(clue) { - if (previousClueSelection !== null && isEquivalent(previousClueSelection, clue)) { - unsetClue(clue.number, clue.direction); - blockHighlight = false; - previousClueSelection = null; - return false; + this._cluesElHeight = height1; + this._cluesElWidth = width1 * scale; + this._height = height1 * scale; + this._scale = scale; + + magicInput.style.display = 'none'; + + //update grid size to fill 100% on mobile view + const fullWidth = Math.min(window.innerHeight, window.innerWidth); + this.rootEl.width = fullWidth + 'px !important'; + const gridTDs = gridEl.querySelectorAll('td'); + const gridSize = gridEl.querySelectorAll('tr').length; + const newTdWidth = parseInt(fullWidth / (gridSize + 1) ); + const inputEl = document.querySelector('.o-crossword-magic-input'); + + if(isMobile) { + for (let i = 0; i < gridTDs.length; i++) { + let td = gridTDs[i]; + td.style.width = Math.min(newTdWidth, cellSizeMax) + "px"; + td.style.height = Math.min(newTdWidth, cellSizeMax) + "px"; + td.style.maxWidth = "initial"; + td.style.minWidth = "initial"; + } + + inputEl.style.width = Math.min(newTdWidth, cellSizeMax) + "px"; + inputEl.style.height = Math.min(newTdWidth, cellSizeMax) + "px"; + inputEl.style.maxWidth = "initial"; + } else { + for (let i = 0; i < gridTDs.length; i++) { + let td = gridTDs[i]; + td.style.removeProperty('width'); + td.style.removeProperty('height'); + td.style.removeProperty('max-width'); + td.style.removeProperty('min-width'); + } + + let desktopSize = gridTDs[0].getBoundingClientRect().width; + inputEl.style.width = desktopSize + "px"; + inputEl.style.height = desktopSize + "px"; } - blockHighlight = true; - previousClueSelection = clue; + d2 = gridEl.getBoundingClientRect(); + clueDisplayer.style.width = d2.width + 'px'; - return true; + }.bind(this); + + if(!isAndroid()) { + this.onResize = debounce(onResize, 100); } const onTap = function onTap(e) { @@ -1056,4 +1073,22 @@ function isiOS() { function isAndroid() { var android = navigator.userAgent.toLowerCase().indexOf("android") > -1; return android; +} + +function isEquivalent(a, b) { + var aProps = Object.getOwnPropertyNames(a); + var bProps = Object.getOwnPropertyNames(b); + + if (aProps.length != bProps.length) { + return false; + } + + for (var i = 0; i < aProps.length; i++) { + var propName = aProps[i]; + if (a[propName] !== b[propName]) { + return false; + } + } + + return true; } \ No newline at end of file diff --git a/src/scss/_base.scss b/src/scss/_base.scss index 02e11b9..d8ed261 100644 --- a/src/scss/_base.scss +++ b/src/scss/_base.scss @@ -90,7 +90,9 @@ padding: 0; width: 100%; - .sr-direction { + .sr-direction, + .sr-instruction, + .sr-answer { display: none; } } @@ -238,7 +240,9 @@ @include oColorsFor(o-crossword-clue-highlight, text); } - .sr-direction { + .sr-direction, + .sr-instruction, + .sr-answer { display: none; } } From fb703ce6f2d8eecd99ffebd94d7bcac80fdc57ea Mon Sep 17 00:00:00 2001 From: Lily2point0 Date: Thu, 1 Jun 2017 14:34:25 +0100 Subject: [PATCH 10/13] combined blanks for screen reader --- src/js/oCrossword.js | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/js/oCrossword.js b/src/js/oCrossword.js index 451c363..46525a4 100644 --- a/src/js/oCrossword.js +++ b/src/js/oCrossword.js @@ -627,7 +627,6 @@ OCrossword.prototype.assemble = function assemble() { if(!def.classList.contains('has-hover')) { highlightGridByNumber(targetClue.number, targetClue.direction, targetClue.answerLength); } - }); function takeInput(el, nextEls) { @@ -800,12 +799,31 @@ OCrossword.prototype.assemble = function assemble() { ++filledCount; answerValue.push(input.value); } else { - answerValue.push(" blank "); + answerValue.push("."); } }); + let combineCount = 0; + let combinedValue = []; + + for(let i = 0; i < answerValue.length; ++i) { + if(answerValue[i] === '.') { + ++combineCount; + if((i < answerValue.length - 1 && answerValue[i + 1] !== '.') || i === answerValue.length - 1) { + if(combineCount > 1) { + combinedValue.push(" " + combineCount + " blanks "); + } else { + combinedValue.push(" blank "); + } + } + } else { + combineCount = 0; + combinedValue.push(answerValue[i]); + } + } + if(filledCount > 0) { - screenReaderAnswer.textContent = 'Your Answer: ' + answerValue.join('') + '.'; + screenReaderAnswer.textContent = 'Your Answer: ' + combinedValue.join('') + '.'; } else { screenReaderAnswer.textContent = ''; } From a13c5591e0c97a04229ef77b0dc2b9b099d405db Mon Sep 17 00:00:00 2001 From: Lily2point0 Date: Thu, 1 Jun 2017 15:37:08 +0100 Subject: [PATCH 11/13] merge parser changes --- src/js/oCrossword.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/js/oCrossword.js b/src/js/oCrossword.js index 52e332b..63df7a2 100644 --- a/src/js/oCrossword.js +++ b/src/js/oCrossword.js @@ -123,7 +123,7 @@ function buildGrid( let count = 0; - if(across[3].length > 1) { + if(across[3].length > 1) { for(var j = 0; j < across[3].length; ++j) { if(j%2 === 1) { count += parseInt(across[3][j-1]); @@ -177,7 +177,7 @@ function buildGrid( let count = 0; - if(down[3].length > 1) { + if(down[3].length > 1) { for(var j = 0; j < down[3].length; ++j) { if(j%2 === 1) { count += parseInt(down[3][j-1]); From 2ea87edfd9105a914e87135103f25897dcbd610e Mon Sep 17 00:00:00 2001 From: Lily2point0 Date: Thu, 1 Jun 2017 15:54:28 +0100 Subject: [PATCH 12/13] rem test timeout --- src/js/oCrossword.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/oCrossword.js b/src/js/oCrossword.js index 63df7a2..68617cf 100644 --- a/src/js/oCrossword.js +++ b/src/js/oCrossword.js @@ -472,7 +472,7 @@ OCrossword.prototype.assemble = function assemble() { if (!isAndroid()) { e.preventDefault(); - timer = 100; + timer = 0; } if(e.target.nodeName !== 'INPUT') { From 85c1b137a6a31781653a4a0557afc807ae3c668d Mon Sep 17 00:00:00 2001 From: Lily2point0 Date: Thu, 1 Jun 2017 16:36:28 +0100 Subject: [PATCH 13/13] add grid tabbing --- src/js/oCrossword.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/js/oCrossword.js b/src/js/oCrossword.js index 68617cf..d0bc4ca 100644 --- a/src/js/oCrossword.js +++ b/src/js/oCrossword.js @@ -389,6 +389,7 @@ OCrossword.prototype.assemble = function assemble() { let blockHighlight = false; let previousClueSelection = null; + let isTab = false; function constructInputIdentifier(data, direction) { let identifier; @@ -412,7 +413,8 @@ OCrossword.prototype.assemble = function assemble() { } if(e.shiftKey && e.keyCode === 9) { - return progress(-1); + isTab = true; + return clueNavigationPrev.click(); } if (e.keyCode === 13) { //enter @@ -420,8 +422,13 @@ OCrossword.prototype.assemble = function assemble() { return progress(); } + if (e.keyCode === 9) { //tab + //TODO: get next clue; + isTab = true; + return clueNavigationNext.click(); + } + if ( - e.keyCode === 9 || //tab e.keyCode === 40 ||//down e.keyCode === 39 ||//right e.keyCode === 32 //space @@ -1007,13 +1014,15 @@ OCrossword.prototype.assemble = function assemble() { currentlySelectedGridItem.answerLength ); - if(!isNavigation) { + if(!isNavigation || isTab) { takeInput(cell, getGridCellsByNumber( gridEl, currentlySelectedGridItem.number, currentlySelectedGridItem.direction, currentlySelectedGridItem.answerLength )); + + isTab = false; } } }.bind(this);