Skip to content

Commit

Permalink
ucfopen#9 Preliminary support for screen readers with the clue list i…
Browse files Browse the repository at this point in the history
…s moving along.
  • Loading branch information
FrenjaminBanklin committed May 13, 2019
1 parent 2c5fe38 commit 90f1fed
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 12 deletions.
92 changes: 89 additions & 3 deletions src/player.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ Namespace('Crossword').Engine = do ->
# variables to store widget data in this scope
_qset = null
_questions = null
_usedHints = []
_freeWordsRemaining = 0
_puzzleGrid = {}
_instance = {}
Expand Down Expand Up @@ -34,6 +35,8 @@ Namespace('Crossword').Engine = do ->
_prevDir = 0
# the current letter that is highlighted
_curLetter = false
# the current clue that is selected
_curClue = 0

# cache DOM elements for performance
_domCache = {}
Expand All @@ -56,6 +59,10 @@ Namespace('Crossword').Engine = do ->
BOARD_LETTER_HEIGHT = Math.floor(BOARD_HEIGHT / LETTER_HEIGHT)
NEXT_RECURSE_LIMIT = 8 # number of characters in a row we'll try to jump forward before dying

BASE_CLUE_TEXT = 'Use the Up and Down arrow keys to navigate between clues. Press the space bar to automatically select the answer grid and move to the first letter tile for this word. '
HINT_CLUE_TEXT = 'Press the H key to receive a hint and reduce this answer\'s value by '
FREE_WORD_CLUE_TEXT = 'Press the F key to have this word\'s letters filled in automatically. '

# Called by Materia.Engine when your widget Engine should start the user experience.
start = (instance, qset, version = '1') ->
# if we're on a mobile device, some event listening will be different
Expand All @@ -71,6 +78,8 @@ Namespace('Crossword').Engine = do ->
_instance = instance
_qset = qset

HINT_CLUE_TEXT += qset.options.hintPenalty + '%. '

# easy access to questions
_questions = _qset.items[0].items
_boardDiv = $('#movable')
Expand Down Expand Up @@ -145,7 +154,9 @@ Namespace('Crossword').Engine = do ->
# keep focus on the last letter that was highlighted whenever we move the board around
$('#board').click -> _highlightPuzzleLetter false

$('#board').keydown _keydownHandler
$('#board').focus -> _boardFocused

$('#board').keydown _boardKeyDownHandler
$('#printbtn').click (e) ->
Crossword.Print.printBoard(_instance, _questions)
$('#zoomout').click _zoomOut
Expand All @@ -171,6 +182,9 @@ Namespace('Crossword').Engine = do ->
document.getElementById('board').addEventListener 'mousemove', _mouseMoveHandler
document.addEventListener 'mouseup', _mouseUpHandler

$('#clues').keydown _clueKeyDownHandler
$('#clues').focus _cluesFocused

# start dragging
_mouseDownHandler = (e) ->
context = if _isMobile then e.pointers[0] else e
Expand Down Expand Up @@ -248,6 +262,7 @@ Namespace('Crossword').Engine = do ->
_wordIntersections[location] = intersection
_labelIndexShift += 1
hintPrefix = _wordMapping[location] + (if dir then ' down' else ' across')

_renderClue questionText, hintPrefix, i, dir

_prevDir = dir if ~~i == 0
Expand Down Expand Up @@ -277,7 +292,9 @@ Namespace('Crossword').Engine = do ->
letterElement = document.createElement if protectedSpace then 'div' else 'input'
letterElement.id = "letter_#{letterLeft}_#{letterTop}"
letterElement.classList.add 'letter'
letterElement.setAttribute 'tabindex', '-1'
letterElement.setAttribute 'readonly', true
letterElement.setAttribute 'aria-hidden', true
letterElement.setAttribute 'data-q', i
letterElement.setAttribute 'data-dir', dir
letterElement.onclick = _letterClicked
Expand Down Expand Up @@ -430,7 +447,69 @@ Namespace('Crossword').Engine = do ->
else
_curLetter.x--

_keydownHandler = (keyEvent, iteration = 0) ->
_clueKeyDownHandler = (keyEvent) ->
console.log keyEvent.keyCode
keyEvent.preventDefault
switch keyEvent.keyCode
when 32 #space
_clueMouseUp {target: $('#clue_'+_curClue)[0]}
when 38 #up
_changeSelectedClue true
when 40 #down
_changeSelectedClue false

_boardFocused = () ->
_dom('board-reader').innerHTML = ''
_updateClue()

_cluesFocused = () ->
_dom('clue-reader').innerHTML = ''
_selectCurrentClue()

_changeSelectedClue = (up) ->
# _dom('clue_'+_curClue).classList.remove 'highlight'
_clueMouseOut {target: $('#clue_'+_curClue)[0]}

if up
_curClue--
if _curClue < 0 then _curClue = _questions.length - 1
else
_curClue++
if _curClue >= _questions.length then _curClue = 0

_selectCurrentClue()

_selectCurrentClue = ->
$('#clues .highlight').removeClass 'highlight'
clueElement = _dom('clue_'+_curClue)
clueElement.classList.add 'highlight'

clue = _questions[_curClue]

combinedClueText = 'Word ' + (_curClue + 1) + ' of ' + _questions.length + '. '
combinedClueText += _questions[_curClue].prefix + '. '
combinedClueText += _ensurePeriod clue.questions[0].text

unless clue.options.hint is ''
if _usedHints[_curClue]
combinedClueText += 'Hint: ' + _ensurePeriod clue.options.hint
else
combinedClueText += HINT_CLUE_TEXT + '. '
unless _freeWordsRemaining is 0
plural = if _freeWordsRemaining > 1 then 'words' else 'word'
combinedClueText += ' ' + FREE_WORD_CLUE_TEXT + 'You have ' + _freeWordsRemaining + ' free ' + plural + ' remaining. '
combinedClueText += ' ' + BASE_CLUE_TEXT

_dom('clue-reader').innerHTML = combinedClueText

_dom('clues').scrollTop = clueElement.offsetTop

_ensurePeriod = (text) ->
unless text[text.length - 1] == '.'
return text + '. '
text

_boardKeyDownHandler = (keyEvent, iteration = 0) ->
return if keyEvent.altKey
_lastLetter = {}
_lastLetter.x = _curLetter.x
Expand Down Expand Up @@ -534,7 +613,7 @@ Namespace('Crossword').Engine = do ->
if iteration == (NEXT_RECURSE_LIMIT - 2) and keyEvent.keyCode >= 48
# simulates enter being pressed after a letter typed in last slot
keyEvent.keyCode = 13
_keydownHandler(keyEvent, (iteration || 0)+1)
_boardKeyDownHandler(keyEvent, (iteration || 0)+1)
return
else
# highlight the last successful letter
Expand Down Expand Up @@ -678,6 +757,7 @@ Namespace('Crossword').Engine = do ->

# called after confirm dialog
_getHint = (index) ->
_usedHints[index] = true
Materia.Score.submitInteractionForScoring _questions[index].id, 'question_hint', '-' + _qset.options.hintPenalty

hintSpot = _dom("hintspot_#{index}")
Expand Down Expand Up @@ -715,6 +795,7 @@ Namespace('Crossword').Engine = do ->
numberLabel = document.createElement 'div'
numberLabel.innerHTML = questionNumber
numberLabel.classList.add 'numberlabel'
numberLabel.setAttribute 'aria-hidden', true
numberLabel.style.top = y * LETTER_HEIGHT + 'px'
numberLabel.style.left = x * LETTER_WIDTH + 'px'
numberLabel.onclick = -> _letterClicked target: $("#letter_#{x}_#{y}")[0]
Expand All @@ -725,6 +806,9 @@ Namespace('Crossword').Engine = do ->
clue = document.createElement 'div'
clue.id = 'clue_' + i

# store the '# across/down' information in the question for later use
_questions[i].prefix = hintPrefix

clue.innerHTML = $('#t_hints').html()
.replace(/{{hintPrefix}}/g, hintPrefix)
.replace(/{{question}}/g, question)
Expand All @@ -733,6 +817,8 @@ Namespace('Crossword').Engine = do ->

clue.setAttribute 'data-i', i
clue.setAttribute 'data-dir', dir
clue.setAttribute 'aria-hidden', true
clue.setAttribute 'role', 'listitem'
clue.classList.add 'clue'

clue.onmouseover = _clueMouseOver
Expand Down
43 changes: 34 additions & 9 deletions src/player.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,44 @@
</head>

<body>
<div class="header">
<div class="header"
aria-labelledby='title'
aria-describedby='instructions'
tabindex='1'>
<div class='logo'></div>
<h1 id='title'></h1>
<div class='icon-print' id='printbtn' title='Print'></div>
<div class='icon-zoomout' id='zoomout' title='Zoom Out'></div>
<div class='icon-print' id='printbtn' title='Print' aria-hidden></div>
<div class='icon-zoomout' id='zoomout' title='Zoom Out' aria-hidden></div>
<div id='instructions'
class='reader-instructions'>
Use the Tab key to switch between the list of words and the answer grid.
</div>
<div class='submitholder'>
<input type='button' value='Submit Answers' id='checkBtn' class='button submit'>
<input type='button' id='checkBtn' class='button submit' value='Submit Answers'>
</div>
</div>
<div id="board" class="screen board">
<div id='board-reader' class='reader-instructions' aria-live='polite'></div>
<div id="board" class="screen board"
aria-describedby='board-reader'
tabindex='3'>
<div id="movable-container">
<div id='movable' class='crossword-board'></div>
</div>
</div>

<div id='clues'>
<div id='clue-reader' class='reader-instructions' aria-live='polite'></div>
<div id='clues'
role='list'
aria-describedby='clue-reader'
tabindex='2'>
<span style='display:none'>Clues</span>
</div>

<div id='controlbar'>
<span id='freeWordsRemaining'></span>
<span id='freeWordsRemaining'
tabindex='-1'
aria-hidden>
</span>
</div>
<div id='specialInput' class='down'>
<div id='specialInputHead'>Special Characters</div>
Expand Down Expand Up @@ -89,8 +106,16 @@ <h1 id='title'></h1>
<div id='t_hints' style='display:none' class="clue">
<em data-i='{{i}}'>{{hintPrefix}}:</em><br>
{{question}}<br>
<div class='hint button' data-i='{{i}}' data-dir='{{dir}}' id='hintbtn_{{i}}'>Hint</div>
<div class='free-word button' data-i='{{i}}' data-dir='{{dir}}' id='freewordbtn_{{i}}'>Free word</div>
<div id='hintbtn_{{i}}'
class='hint button'
data-i='{{i}}'
data-dir='{{dir}}'
aria-hidden>Hint</div>
<div id='freewordbtn_{{i}}'
class='free-word button'
data-i='{{i}}'
data-dir='{{dir}}'
aria-hidden>Free word</div>
<span class='hintspot' id='hintspot_{{i}}'></span>
</div>
<div class="arrow_box">Click to finish</div>
Expand Down
11 changes: 11 additions & 0 deletions src/player.scss
Original file line number Diff line number Diff line change
Expand Up @@ -525,3 +525,14 @@ input {
display: inline-block;
margin-right: 20px;
}

.reader-instructions {
position: absolute;
margin: -1px;
border: 0;
padding: 0;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
}

0 comments on commit 90f1fed

Please sign in to comment.