---
# Documentation:
#
# The general form for many rules is:
# 1. Say the command if this is first rule to fire (MatchCounter) and depending upon "NavVerbosity"s value
# This will increment MatchCounter so that the command won't be spoken again
# 2. Say info about moving into/out of 2D structures
# 3. Set some variables and possibly recurse.
# If stopping, "NavNode" should be set.
#
# The meaning of NavVerbosity:
# * Verbose -- always echo the command and end points (e.g., "can't move right")
# * Medium -- only echo the command for obscure commands like the placemarker commands; also say end points
# * Terse -- no echo of commands or end points
#
# For the second item, a common set of rules is used. These rules require the variable "Move2D"
# to be set along with "Child2D", where "Move2D" is either 'in' or 'out'.
#
# In addition, the navigation rules make use of two functions:
#
# int DistanceFromLeaf(node, leftSide, treat2DElementsAsTokens)
# Returns the distance (number of children) until a leaf is reached by traversing the leftmost/rightmost child
# If 'treat2DElementsAsTokens' is true, then 2D notations such as fractions are treated like leaves
# A leaf has distance == 0
#
# EdgeNode(node, "left"/"right", stopNodeName)
# Returns the stopNode if at left/right edge of named ancestor node. 'stopNodeName' can also be "2D"
# If the stopNode isn't found, the original node is returned
# Note: if stopNodeName=="math", then punctuation is taken into account since it isn't really part of the math
#
# A few other variables are of importance to Navigation
# NavMode -- Enhanced, Simple, Character
# ReadZoomLevel -- -1 for Enhanced, otherwise the distance from leaf the rules should maintain
# PlaceMarkerIndex
# Note: the rules for saying a command and announcing what is said when moving in/out of a 2d exprs are hacks
# They depend upon special variables "SayCommand" and "Move2D" being set and if they are, the rules are activated.
# If/when functions can be defined in a rules file, it is likely these would be much better done via those functions
# as they would likely be much more efficient and also cleaner.
# Rules for announcing the command
- name: say-command
tag: "!*"
match: "$SayCommand != ''" # value should be '', 'true', or 'false'
variables: [Prefix: "''"]
replace:
- test:
- if: "$MatchCounter = 0 and $SayCommand = 'true'"
then_test:
- if: "self::m:math and starts-with($NavCommand, 'ZoomOut')"
then: [t: "zoomed out all the way", pause: "medium"]
- else_if: "IsNode(., 'leaf') and starts-with($NavCommand, 'ZoomIn')"
then: [t: "zoomed in all the way", pause: "medium"]
else:
- test:
- if: "starts-with($NavCommand, 'Zoom')"
then: [set_variables: [Prefix: "'zoom'"]] # phrase('zoom' in to see more details)
- else_if: "starts-with($NavCommand, 'Move')"
then: [set_variables: [Prefix: "'move'"]] # phrase('move' to next entry in table)
- else_if: "starts-with($NavCommand, 'Read')"
then: [set_variables: [Prefix: "'read'"]] # phrase('read' to next entry in table)
- else_if: "starts-with($NavCommand, 'Describe')"
then: [set_variables: [Prefix: "'describe'"]] # phrase('describe' to next entry in table)
- test:
if: "$Prefix != ''"
then:
- x: "$Prefix"
- test:
- if: "substring($NavCommand, string-length($Prefix)+1) = 'In'"
then: [t: "in"] # phrase(zoom 'in' to see more details)
- else_if: "substring($NavCommand, string-length($Prefix)+1) = 'InAll'"
# HACK: '\uF8FE' is used internally for the concatenation char by 'ct' -- this gets "ed" concatenated to "zoom"
then: [t: "\uF8FEed in all the way"] # phrase(zoom 'out all the way' to see more details)
- else_if: "substring($NavCommand, string-length($Prefix)+1) = 'Out'"
then: [t: "out"] # phrase(zoom 'out' to see more details)
- else_if: "substring($NavCommand, string-length($Prefix)+1) = 'OutAll'"
# HACK: '\uF8FE' is used internally for the concatenation char by 'ct' -- this gets "ed" concatenated to "zoom"
then: [t: "\uF8FEed out all the way"] # phrase(zoom 'out all the way' to see more details)
- else_if: "substring($NavCommand, string-length($Prefix)+1) = 'Next'"
then: [t: "right"] # phrase(move to the 'right')
- else_if: "substring($NavCommand, string-length($Prefix)+1) = 'Previous'"
then: [t: "left"] # phrase(move to the 'left')
- else_if: "substring($NavCommand, string-length($Prefix)+1) = 'Current'"
then: [t: "current"] # phrase(who is the 'current' president)
- else_if: "substring($NavCommand, string-length($Prefix)+1) = 'LineStart'"
then: [t: "to start of line"] # phrase(move 'to start of line')
- else_if: "substring($NavCommand, string-length($Prefix)+1) = 'LineEnd'"
then: [t: "to end of line"] # phrase(move 'to end of line')
- pause: "medium"
- set_variables: [MatchCounter: "$MatchCounter + 1"]
- name: into-or-out-of-silent
tag: "*"
# saying "out of row n" is not very useful, so skip it
match: "$Move2D != '' and (not(@data-from-mathml) or @data-from-mathml = name(.)) and
(name(.)='mrow' or name(.) = 'mtr' or name(.) = 'mlabeledtr' or @data-from-mathml = 'mtable')"
replace: []
- name: into-or-out-of-mtr
tag: [mtr, mlabeledtr]
match: "$Move2D = 'in'"
replace:
- t: "column"
- x: "count($Child2D/preceding-sibling::*)+1"
- pause: "medium"
- name: into-or-out-of-mmultiscripts
tag: "*"
match: "$Move2D != '' and (@data-from-mathml='mmultiscripts' or self::m:mmultiscripts)"
replace:
- test:
if: "name($Child2D)!='none'"
then:
- with:
variables:
- NumPrecedingSiblings: "count($Child2D/preceding-sibling::*)"
replace:
- x: "$Move2D"
- test:
- if: "$NumPrecedingSiblings=0"
then: [t: "base"] # phrase(the 'base' of the power)
- else_if: "$Child2D/preceding-sibling::*[self::m:mprescripts]" # are we before mprescripts and hence are postscripts
then:
- test: # in postscripts -- base shifts by one
if: "$NumPrecedingSiblings mod 2 = 0"
then: [t: "pre-subscript"] # phrase(x with 'subscript' 2)
else: [t: "pre-superscript"] # phrase(x with 'superscript' 2)
else:
- test:
if: "$NumPrecedingSiblings mod 2 = 0"
then: [t: "superscript"] # phrase(x with 'pre-superscript' 2)
else: [t: "subscript"] # phrase(x with 'pre-subscript' 2)
- pause: "medium"
# Rules for speaking what happens when moving into or out of a notation
- name: into-or-out-of-default
tag: "*"
# saying "out of row n" is not very useful, so skip it
# match: "$Move2D != '' and @data-from-mathml and @data-from-mathml != name(.) and count(*)>1 and @data-from-mathml != 'mtable'"
match: "$Move2D != '' and not(self::m:math or @data-from-mathml = 'mtable' or @data-from-mathml = 'mtd') "
replace:
- with:
variables:
- PartNumber: "count($Child2D/preceding-sibling::*)"
- PartName: "GetNavigationPartName(name(.), $PartNumber)"
replace:
- x: "$Move2D"
- test:
- if: "$PartName != ''"
then: [x: "$PartName"]
- else_if: "count(*) = 1"
then_test:
if: "$NavVerbosity = 'Verbose'"
then: [x: "translate(name(.), '-_', ' ')"] # e.g., "in absolute value"
else:
- t: "part" # phrase(the 'part' of the expression)
- x: "count($Child2D/preceding-sibling::*) + 1"
- pause: "medium"
- name: default-move
# nothing to do (not 2D) -- need to catch $Move2D though so rules based on NavCommand don't trigger
tag: "*"
match: "$Move2D != ''"
replace: []
# ********* Go back to last position ***************
# This is first since start/end position shouldn't matter
- name: move-last-location
tag: "*"
match: "$NavCommand = 'MoveLastLocation'"
replace:
- test:
if: "$NavVerbosity != 'Terse'"
then:
- test:
- if: "$PreviousNavCommand = 'ZoomIn'"
then: [t: "undo zoom in"] # phrase('undo zoom in')
- else_if: "$PreviousNavCommand = 'ZoomOut'"
then: [t: "undo zoom out"] # phrase('undo zoom out')
- else_if: "$PreviousNavCommand = 'ZoomInAll'"
then: [t: "undo zooming in all the way"] # phrase('undo zooming in all the way')
- else_if: "$PreviousNavCommand = 'ZoomOutAll'"
then: [t: "undo zooming out all the way"] # phrase('undo zooming out all the way')
- else_if: "$PreviousNavCommand = 'MovePrevious' or $PreviousNavCommand = 'MovePreviousZoom'"
then: [t: "undo move left"] # phrase('undo move left')
- else_if: "$PreviousNavCommand = 'MoveNext' or $PreviousNavCommand = 'MoveNextZoom'"
then: [t: "undo move right"] # phrase('undo move right')
- else_if: "$PreviousNavCommand = 'None'"
then: [t: "no previous command"] # phrase('no previous command')
- pause: "medium"
- set_variables: [NavNode: "@id"]
# many times, for typographic reasons, people include punctuation at the end of a math expr
# these rules detect that and skip speaking it (should be similar regular rule)
- name: skip-punct-at-end-zoom-in
tag: mrow
match:
- "($NavCommand = 'ZoomIn' or $NavCommand = 'ZoomInAll' or $NavCommand = 'MoveNextZoom' or $NavCommand = 'MovePreviousZoom') and"
- " parent::m:math and count(*)=2 and"
- " *[2][translate(.,'.,;:?', '')='']"
replace:
- x: "*[1]"
# ********* ZoomIn ***************
- name: zoom-in-leaf
tag: "*"
match: "($NavCommand = 'ZoomIn' or $NavCommand = 'ZoomInAll') and IsNode(., 'leaf')"
replace:
- test:
if: "$MatchCounter = 0 and $NavVerbosity != 'Terse'"
then: [t: "zoomed in all of the way", pause: "long"] # phrase('zoomed in all of the way')
- test:
if: "$ReadZoomLevel!=-1"
then:
- set_variables: [ReadZoomLevel: "0"]
- set_variables: [NavNode: "@id"]
# special case of zooming into a table -- move to the first row (if only one row, first column)
- name: zoom-in-table
tag: "*"
match: "$NavCommand = 'ZoomIn' and (name(.) = 'mtable' or (count(*)=1 and *[1][@data-from-mathml='mtable']))"
replace:
- with:
variables: [SayCommand: "string($NavVerbosity = 'Verbose')"]
replace: [x: "."]
- test:
if: "count(*)=1"
then:
- set_variables: [NavNode: "*[1]/*[1]/@id"]
else:
- set_variables: [NavNode: "*[1]/@id"]
- name: zoom-in-mrow-in-math
# zooming in only once is meaningless because 'math' has only a single child and it was spoken at the math level -- dig inside and do it again
tag: math
match: "$NavCommand = 'ZoomIn' or $NavCommand = 'MoveNextZoom' or $NavCommand = 'MovePreviousZoom'"
replace:
- test:
if: "$NavCommand = 'MovePreviousZoom'"
then: [x: "*[last()]"]
else: [x: "*[1]"]
- # For msqrt and menclose, if the single child isn't an mrow, don't zoom in
name: zoom-in-again
# If there is only one child, this isn't an interesting move -- zoom in again
tag: "*"
match:
- "($NavCommand = 'ZoomIn' or "
- " ($NavCommand = 'MoveNextZoom' or $NavCommand = 'MovePreviousZoom') and $NavMode='Enhanced') and "
- "count(*)=1 and
(*[1][self::m:mrow or @data-from-mathml='mrow'] and
not(@data-from-mathml='msqrt' or self::m:msqrt or @data-from-mathml='menclose' or self::m:menclose))"
replace:
- with:
variables: [SayCommand: "string($NavVerbosity = 'Verbose')"]
replace: [x: "."]
- test:
if: "name(*[1]) != 'mrow'"
then:
- with:
variables: [Move2D: "'in'", Child2D: "IfThenElse(count(*)=0, $Move2D, $Move2D)"] # phrase('in' the denominator)
replace: [x: "IfThenElse($NavCommand = 'MovePreviousZoom', 1, $Child2D)"]
- test:
if: "$NavCommand = 'MovePreviousZoom'"
then: [x: "*[last()]"]
else: [x: "*[1]"]
- name: zoom-in-enhanced
tag: "*"
match: "$NavCommand = 'ZoomIn' and $NavMode='Enhanced'"
replace:
- with:
variables: [SayCommand: "string($NavVerbosity = 'Verbose')"]
replace: [x: "."]
- test:
- if: "self::m:mtr or self::m:mlabeledtr"
then:
- with:
variables: [Move2D: "'in'", Child2D: "*[1]/*[1]"] # phrase('in' the denominator)
replace: [x: "."]
- set_variables: [NavNode: "*[1]/*[1]/@id"] # skip mtd
- else_if: "*[1][self::m:mrow and (IsBracketed(., '(', ')', false) or IsBracketed(., '[', ']', false))]" # auto zoom
then:
- with:
variables: [Move2D: "'in'", Child2D: "*[1]"] # phrase('in' the denominator)
replace: [x: "."]
- set_variables: [NavNode: "*[1]/*[2]/@id"] # skip parens/brackets
else:
- with:
variables: [Move2D: "'in'", Child2D: "*[1]"] # phrase('in' the denominator)
replace: [x: "."]
- set_variables: [NavNode: "*[1]/@id"]
- name: zoom-in-simple
tag: "*"
match: "$NavCommand = 'ZoomIn' and $NavMode='Simple'"
replace:
- with:
variables: [SayCommand: "string($NavVerbosity = 'Verbose')"]
replace: [x: "."]
- test:
if: "DEBUG($MatchCounter) > 1 and IsNode(., '2D') "
then: [set_variables: [NavNode: "@id"]] # time to stop, not going "in" to next thing, so before "Move2D"
else:
- with:
variables: [Move2D: "'in'", Child2D: "*[1]"] # phrase('in' the denominator)
replace: [x: "."]
- x: "*[1]"
# At this point, we are zooming in on a non-2D element, non-leaf in Character mode
- name: zoom-in-2D-character
tag: "*"
match: "$NavCommand = 'ZoomIn' and (IsNode(., '2D') or not(IsNode(., 'mathml')))"
replace:
- with:
variables: [SayCommand: "string($NavVerbosity = 'Verbose')"]
replace: [x: "."]
- with:
variables: [Move2D: "'in'", Child2D: "*[1]"] # phrase('in' the denominator)
replace: [x: "."]
- test:
if: "$NavMode = 'Simple'"
then:
- set_variables: [NavNode: "*[1]/@id"]
else:
- with:
variables: [NavCommand: "'MoveNextZoom'"]
replace: [x: "*[1]"]
# At this point, we are zooming in on a non-2D element, non-leaf in Character mode
- name: zoom-in-default
tag: "*"
match: "$NavCommand = 'ZoomIn'"
replace:
- with:
variables: [SayCommand: "string($NavVerbosity = 'Verbose')"]
replace: [x: "."]
- x: "*[1]"
- name: zoom-in-all-default
tag: "*"
match: "$NavCommand = 'ZoomInAll'"
replace:
- with:
variables: [SayCommand: "string($NavVerbosity = 'Verbose')"]
replace: [x: "."]
- with:
variables: [Move2D: "'in'", Child2D: "*[1]"] # phrase('in' the denominator)
replace: [x: "."]
- x: "*[1]"
- name: zoom-out
tag: math
match: "$NavCommand = 'ZoomOut' or $NavCommand = 'ZoomOutAll'"
replace:
- with:
variables: [SayCommand: "string($NavVerbosity = 'Verbose')"]
replace: [x: "."]
- set_variables: [NavNode: "*[1]/@id"] # no-op for $NavCommand = 'ZoomOut'
- name: skip-punct-at-end-zoom-out
tag: mrow
match:
- "($NavCommand = 'ZoomOut' or $NavCommand = 'ZoomOutAll') and"
- " parent::m:math and count(*)=2 and"
- " *[2][translate(.,'.,;:?', '')='']"
replace:
- x: ".."
- name: zoom-out-top
tag: "*"
match:
- "($NavCommand = 'ZoomOut' or $NavCommand = 'ZoomOutAll') and"
- "parent::m:math "
replace:
- x: ".." # let math rule deal with it
- name: zoom-out-all-default
tag: "*"
match: "$NavCommand = 'ZoomOutAll'"
replace:
- with:
variables: [SayCommand: "string($NavVerbosity = 'Verbose')"]
replace: [x: "."]
- with:
variables: [Move2D: "'out of'", Child2D: "."]
replace: [x: ".."]
- x: ".."
# deal with internal zooming: MoveNextZoom and MovePreviousZoom
# start with Enhanced mode
- name: move-zoom-enhanced
tag: "*"
match:
- "($NavCommand = 'MoveNextZoom' or $NavCommand = 'MovePreviousZoom') and "
- "$NavMode = 'Enhanced'"
replace:
- with:
variables: [Move2D: "'in'", Child2D: "*[1]"] # phrase('in' the denominator)
replace: [x: "."]
- test:
- if: "count(*)> 1 or IsNode(., 'leaf') or
@data-from-mathml='msqrt' or self::m:msqrt or @data-from-mathml='menclose' or self::m:menclose"
then: [set_variables: [NavNode: "@id"]]
else: [x: "*[1]"]
- name: move-next-zoom-not-enhanced
# $ReadZoomLevel must be >= 0
tag: "*"
match: "$NavCommand = 'MoveNextZoom'"
replace:
#don't bother with MatchCounter since we only get here if > 1
- test:
if: "IsNode(., 'leaf') or $ReadZoomLevel >= DistanceFromLeaf(., false, $NavMode!='Character')"
then:
# - with:
# variables: [Move2D: "'in'", Child2D: "following-sibling::*[1]"] # phrase('in' the denominator)
# replace: [x: ".."]
- set_variables: [NavNode: "@id"]
else:
- with:
variables: [Move2D: "'in'", Child2D: "*[1]"] # phrase('in' the denominator)
replace: [x: "."]
- x: "*[1]"
- name: move-previous-zoom-not-enhanced
# $ReadZoomLevel must be >= 0
tag: "*"
match: "$NavCommand = 'MovePreviousZoom'"
replace:
#don't bother with MatchCounter since we only get here if > 1
- test:
if: "$ReadZoomLevel >= DistanceFromLeaf(., true, $NavMode!='Character')"
then:
# - with:
# variables: [Move2D: "'in'", Child2D: "preceding-sibling::*[1]"] # phrase('in' the denominator)
# replace: [x: ".."]
- set_variables: [NavNode: "@id"]
else:
- with:
variables: [Move2D: "'in'", Child2D: "*[last()]"] # phrase('in' the denominator)
replace: [x: "."]
- x: "*[last()]"
# ********* ZoomOut ***************
- name: zoom-out-default
tag: mtd
match: "$Move2D = '' and ($NavCommand = 'ZoomOut')"
replace:
- with:
variables: [SayCommand: "string($NavVerbosity = 'Verbose')"]
replace: [x: "."]
# we need to speak it here
# - t: "row" # phrase(the first 'row' of the matrix)
# # if we let the speech rules speak the row, it is given just the MathML for the row, so the row # will always be '1'
# - x: "count(../preceding-sibling::*)+1"
# - pause: medium
- set_variables: [NavNode: "../@id"]
- name: zoom-out
# a row around a single element -- these might duplicate the position/offset, so we jump an extra level here
tag: "*"
match: "$NavCommand = 'ZoomOut'"
replace:
- with:
variables: [SayCommand: "string($NavVerbosity = 'Verbose')"]
replace: [x: "."]
- test:
if: "$NavMode='Enhanced' and parent::*[self::m:mrow and (IsBracketed(., '(', ')', false) or IsBracketed(., '[', ']', false))]"
then: [x: ".."] # auto-zoom: move out a level and retry
else:
- with:
variables: [Move2D: "'out of'", Child2D: "."]
replace: [x: ".."]
- test:
if: "parent::m:mtd"
then: [x: ".."]
else:
- test:
if: "$ReadZoomLevel!=-1"
then: [set_variables: [ReadZoomLevel: "DistanceFromLeaf(.., true, $NavMode!='Character')"]]
- set_variables: [NavNode: "../@id"]
# ********* MoveStart/End ***************
- name: math-move-to-start-or-end
tag: math
match: "$NavCommand = 'MoveStart' or $NavCommand = 'MoveLineStart' or $NavCommand = 'MoveEnd' or $NavCommand = 'MoveLineEnd'"
replace:
- with:
variables: [MatchCounter: "$MatchCounter + 1"]
replace:
- test:
if: "$NavVerbosity = 'Verbose'"
then:
- test:
- if: "$NavCommand = 'MoveStart'"
then: [t: "move to start of math"] # phrase('move to start of math')
- else_if: "$NavCommand = 'MoveLineStart'"
then: [t: "move to start of line"] # phrase('move to start of line')
- else_if: "$NavCommand = 'MoveEnd'"
then: [t: "move to end of math"] # phrase('move to end of math')
else: [t: "move to end of line"] # "$NavCommand = 'MoveLineEnd'" # phrase('move to end of line')
- pause: "medium"
- test:
if: "$NavCommand = 'MoveStart' or $NavCommand = 'MoveLineStart'"
then:
# move inside of the mrow inside of 'math' or inside the fraction, etc (hence two levels down)
# Note: an apparent bug in the xpath code doesn't let me use IfThenElse for the 2 if: then: below
- with:
variables: [NavCommand: "'MoveNextZoom'"]
replace:
- test:
if: "*[1]/*[1]" # could be a , so no grandchild
then: [x: "*[1]/*[1]"]
else: [x: "*[1]"]
else:
- with:
variables: [NavCommand: "'MovePreviousZoom'"]
replace:
- test:
if: "*[last()]/*[last()]" # could be a , so no grandchild
then: [x: "*[last()]/*[last()]"]
else: [x: "*[last()]"]
# We stop when the parent is 2d (e.g., frac), but not if in leaf base of msub/msup/msubsup/mmultiscripts because that's really on the same line
- name: move-to-start-or-end-2d
tag: "*"
match:
- "($NavCommand = 'MoveLineStart' or $NavCommand = 'MoveLineEnd') and IsNode(.., '2D') and"
- "not( IsNode(., 'leaf') and"
- " parent::*[1][self::m:msub or self::m:msup or self::m:msubsup or self::m:mmultiscripts or"
- " @data-from-mathml and"
- " (@data-from-mathml='msub' or @data-from-mathml='msup' or"
- " @data-from-mathml='msubsup' or @data-from-mathml='mmultiscripts')"
- " ] )"
replace:
- test:
if: "$NavVerbosity = 'Verbose'"
then:
- test:
if: "$NavCommand = 'MoveLineStart'"
then: [t: "move to start of line"] # phrase('move to start of line')
else: [t: "move to end of line"] # "$NavCommand = 'MoveLineEnd'" # phrase('move to end of line')
- pause: "medium"
- test:
if: "self::m:mrow or @data-from-mathml = 'mrow'"
then_test:
if: "$NavCommand = 'MoveLineStart'"
then:
- with:
variables: [NavCommand: "'MoveNextZoom'"]
replace: [x: "*[1]"]
else:
- with:
variables: [NavCommand: "'MovePreviousZoom'"]
replace: [x: "*[last()]"]
else: [set_variables: [NavNode: "@id"]]
- name: move-to-start-or-end-default
tag: "*"
match: "$NavCommand = 'MoveStart' or $NavCommand = 'MoveLineStart' or $NavCommand = 'MoveEnd' or $NavCommand = 'MoveLineEnd'"
replace:
- with:
variables: [MatchCounter: "$MatchCounter + 1"]
replace: [x: ".."]
# Table-related movement
# Typically, we need to zoom out to the mtd level, then we move the appropriate direction
- name: not-in-table
tag: math
match:
- "$NavCommand='MoveCellPrevious' or $NavCommand='MoveCellNext' or"
- "$NavCommand='MoveCellUp' or $NavCommand='MoveCellDown' or"
- "$NavCommand='MoveColumnStart' or $NavCommand='MoveColumnEnd' or"
- "$NavCommand='ReadCellCurrent'"
replace:
- t: "not in table" # phrase('not in table')
- pause: long
- set_variables: [SpeakExpression: "'false'"]
- name: move-cell-previous
tag: mtd
match: "$NavCommand='MoveCellPrevious'"
replace:
- test:
if: "preceding-sibling::*"
then:
- test:
if: "$NavVerbosity = 'Verbose'"
then:
- t: "move left" # phrase('move left')
- pause: short
- test:
if: "$NavVerbosity != 'Terse'"
then:
- t: "column" # phrase(the first 'column' of the table)
- x: "count(preceding-sibling::*)"
- pause: medium
- test:
if: "$NavMode='Character'"
then:
- with:
variables: [NavCommand: "'MovePreviousZoom'"]
replace: [x: "preceding-sibling::*[1]"]
else:
- set_variables: [NavNode: "preceding-sibling::*[1]/*[1]/@id"]
else:
- t: "no previous column" # phrase('no previous column' in the table)
- set_variables: [SpeakExpression: "'false'"]
- name: move-cell-next
tag: mtd
match: "$NavCommand='MoveCellNext'"
replace:
- test:
if: "following-sibling::*"
then:
- test:
if: "$NavVerbosity = 'Verbose'"
then:
- t: "move right" # phrase('move right')
- pause: short
- test:
if: "$NavVerbosity != 'Terse'"
then:
- t: "column" # phrase(the first 'column' in the table)
- x: "count(preceding-sibling::*)+2"
- pause: medium
- test:
if: "$NavMode='Character'"
then:
- with:
variables: [NavCommand: "'MoveNextZoom'"]
replace: [x: "following-sibling::*[1]"]
else:
- set_variables: [NavNode: "following-sibling::*[1]/*[1]/@id"]
else:
- t: "no next column" # phrase('no next column' in the table)
- set_variables: [SpeakExpression: "'false'"]
- name: move-cell-up
tag: mtd
match: "$NavCommand='MoveCellUp'"
replace:
- test:
if: "../preceding-sibling::*"
then:
- with:
variables: [Column: "count(preceding-sibling::*)+1"] # store this because otherwise the value is used in the wrong context below
replace:
- test:
if: "$NavVerbosity = 'Verbose'"
then:
- t: "move up" # phrase('move up' to previous row in the table)
- pause: short
- test:
if: "$NavVerbosity != 'Terse'"
then:
- t: "row" # phrase(the previous 'row' in the table)
- x: "count(../preceding-sibling::*)"
- pause: short
- t: "column" # phrase(the previous 'column' in the table)
- x: "count(preceding-sibling::*)+1"
- pause: medium
- test:
if: "$NavMode='Character'"
then:
- with:
variables: [NavCommand: "'MoveNextZoom'"]
replace: [x: "../preceding-sibling::*[1]/*[$Column]"]
else:
- set_variables: [NavNode: "../preceding-sibling::*[1]/*[$Column]/*[1]/@id"]
else:
- t: "no previous row" # phrase('no previous row' in the table)
- set_variables: [SpeakExpression: "'false'"]
- name: move-cell-down
tag: mtd
match: "$NavCommand='MoveCellDown'"
replace:
- test:
if: "../following-sibling::*"
then:
- with:
variables: [Column: "count(preceding-sibling::*)+1"] # store this because otherwise the value is used in the wrong context below
replace:
- test:
if: "$NavVerbosity = 'Verbose'"
then:
- t: "move down" # phrase('move down to the next row in the table)
- pause: short
- test:
if: "$NavVerbosity != 'Terse'"
then:
- t: "row" # phrase(the next 'row' in the table)
- x: "count(../preceding-sibling::*)+2"
- pause: short
- t: "column" # phrase(the next 'column' in the table)
- x: "count(preceding-sibling::*)+1"
- pause: medium
- test:
if: "$NavMode='Character'"
then:
- with:
variables: [NavCommand: "'MoveNextZoom'"]
replace: [x: "../following-sibling::*[1]/*[$Column]"]
else:
- set_variables: [NavNode: "../following-sibling::*[1]/*[$Column]/*[1]/@id"]
else:
- t: "no next row" # phrase('no next row' in the table)
- set_variables: [SpeakExpression: "'false'"]
- name: move-cell-up
tag: [mtr, mlabeledtr]
match: "$NavCommand='MoveCellUp'"
replace:
- test:
if: "$NavVerbosity = 'Verbose'"
then:
- t: "move to previous row" # phrase('move to previous row' to the previous row in the table)
- pause: medium
- test:
if: "preceding-sibling::*"
then:
- test:
if: "$NavMode='Character'"
then:
- with:
variables: [NavCommand: "'MoveNextZoom'"]
replace: [x: "preceding-sibling::*[1]"]
else:
- set_variables: [NavNode: "preceding-sibling::*[1]/@id"]
else:
- t: "no previous row" # phrase('no previous row' in the table)
- set_variables: [SpeakExpression: "'false'"]
- name: move-cell-down
tag: [mtr, mlabeledtr]
match: "$NavCommand='MoveCellDown'"
replace:
- test:
if: "$NavVerbosity = 'Verbose'"
then:
- t: "move to next row" # phrase('move to next row' to the next row in the table)
- pause: medium
- test:
if: "following-sibling::*"
then:
- test:
if: "$NavMode='Character'"
then:
- with:
variables: [NavCommand: "'MoveNextZoom'"]
replace: [x: "following-sibling::*[1]"]
else:
- set_variables: [NavNode: "following-sibling::*[1]/@id"]
else:
- t: "no next row" # phrase('no next row' in the table)
- set_variables: [SpeakExpression: "'false'"]
- name: move-cell-previous
# if a row is selected, there is no previous/next column, so this is trivial
tag: [mtr, mlabeledtr]
match: "$NavCommand='MoveCellPrevious'"
replace:
- test:
if: "$NavVerbosity = 'Verbose'"
then:
- t: "move to previous column" # phrase('move to previous column' to the previous row in the table)
- pause: medium
- t: "no previous column" # phrase('no previous column' in the table)
- set_variables: [SpeakExpression: "'false'"]
- name: move-cell-next
# if a row is selected, there is no previous/next column, so this is trivial
tag: [mtr, mlabeledtr]
match: "$NavCommand='MoveCellNext'"
replace:
- test:
if: "$NavVerbosity = 'Verbose'"
then:
- t: "move to next column" # phrase('move up' to the previous row in the table)
- pause: medium
- t: "no next column" # phrase('no next row' in the table)
- set_variables: [SpeakExpression: "'false'"]
- name: default-read-cell
tag: "*"
match: "$NavCommand='ReadCellCurrent'"
replace:
- with:
variables: [MTD: "ancestor::m:mtd"]
replace:
- test:
if: "$MTD"
then:
- test:
if: "$NavVerbosity = 'Verbose'"
then:
- t: "read current entry" # phrase('read current entry' in the table)
- pause: medium
- test:
if: "$NavVerbosity != 'Terse'"
then:
- t: "row" # phrase(the previous 'row' in the table)
- x: "count($MTD[1]/../preceding-sibling::*)+1"
- pause: short
- t: "column" # phrase(the previous 'column' in the table)
- x: "count($MTD[1]/preceding-sibling::*)+1"
- pause: short
- set_variables: [NavNode: "$MTD[1]/*[1]/@id"]
else:
- t: "not in table" # phrase('not in table' or matrix)
- pause: long
- set_variables: [SpeakExpression: "'false'"]
# mtd ? ( $NavCommand='MoveColumnStart' )
# => MoveColStart {
# ruleRef = name(^^match);
# column = index(match);
# ::StartPosition = ^^match[0][index(match)][0].dfs;
# ::EndPosition = ^^match[0][index(match)][0].offset;
# };
# mtd ? ( $NavCommand='MoveColumnEnd' )
# => MoveColEnd {
# ruleRef = name(^^match);
# column = index(match);
# ::StartPosition = ^^match[count(^^match)-1][index(match)][0].dfs;
# ::EndPosition = ^^match[count(^^match)-1][index(match)][0].offset;
# };
# # Rules for columnar math (mstack and mlongdiv) -- each row is an msrow or mscarries except for the start of mlongdiv
# # FIX: not dealing with different number of digits on different lines
# # FIX: not dealing with + (etc) on same line if they are on the right side (Dutch, others)
# # FIX: not dealing with intervening msline (say it and move on??)
# # FIX: not dealing with carries well
# # FIX: not dealing with navigation of first three children of mlongdiv
# char ? ( has_parent(match, 2) && name(^match)=="mn" && name(^^match)=="msrow" &&
# ($NavCommand="MovePrevious" || $NavCommand='MoveCellPrevious') && has_previous(match) )
# => MoveCell {
# ruleRef = name(^^match);
# wordRef = "previous";
# ::StartPosition = previous(match).dfs;
# ::EndPosition = previous(match).offset;
# };
# # no previous child -- in first column -- don't move
# char ? ( has_parent(match, 2) && name(^match)=="mn" && name(^^match)=="msrow" &&
# ($NavCommand="MovePrevious" || $NavCommand='MoveCellPrevious' ) )
# => MoveCell {
# ruleRef = name(^^match);
# wordRef = "previous";
# childIndex = -1; # key to know what to say for each notation
# ::SpeakAfterMove = false;
# };
# char ? ( has_parent(match, 2) && name(^match)=="mn" && name(^^match)=="msrow" &&
# ($NavCommand="MoveNext" || $NavCommand='MoveCellNext') && has_next(match) )
# => MoveCell {
# ruleRef = name(^^match);
# wordRef = "next";
# ::StartPosition = next(match).dfs;
# ::EndPosition = next(match).offset;
# };
# # no next child -- in first column -- don't move
# char ? ( has_parent(match, 2) && name(^match)=="mn" && name(^^match)=="msrow" &&
# ($NavCommand="MoveNext" || $NavCommand='MoveCellNext' ) )
# => MoveCell {
# ruleRef = name(^^match);
# wordRef = "next";
# childIndex = -1; # key to know what to say for each notation
# ::SpeakAfterMove = false;
# };
# char ? ( has_parent(match, 2) && name(^match)=="mn" && name(^^match)=="msrow" &&
# $NavCommand='MoveCellUp' && has_previous(^^match) )
# => MoveCell {
# ruleRef = name(^^match);
# wordRef = "up";
# ::StartPosition = ^^^match[index(^^match)-1][-1][index(match)-count(^match)].dfs;
# ::EndPosition = ^^^match[index(^^match)-1][-1][index(match)-count(^match)].offset;
# };
# # no previous child -- in first column -- don't move
# char ? ( has_parent(match, 2) && name(^match)=="mn" && name(^^match)=="msrow" &&
# $NavCommand='MoveCellUp' )
# => MoveCell {
# ruleRef = name(^^match);
# wordRef = "up";
# childIndex = -1; # key to know what to say for each notation
# ::SpeakAfterMove = false;
# };
# char ? ( has_parent(match, 2) && name(^match)=="mn" && name(^^match)=="msrow" &&
# $NavCommand='MoveCellDown' && has_next(^^match) )
# => MoveCell {
# ruleRef = name(^^match);
# wordRef = "down";
# ::StartPosition = ^^^match[index(^^match)+1][-1][index(match)-count(^match)].dfs;
# ::EndPosition = ^^^match[index(^^match)+1][-1][index(match)-count(^match)].offset;
# };
# # no previous child -- in first column -- don't move
# char ? ( has_parent(match, 2) && name(^match)=="mn" && name(^^match)=="msrow" &&
# $NavCommand='MoveCellDown' )
# => MoveCell {
# ruleRef = name(^^match);
# wordRef = "down";
# childIndex = -1; # key to know what to say for each notation
# ::SpeakAfterMove = false;
# };
# char ? ( has_parent(match, 2) && name(^match)=="mn" && name(^^match)=="msrow" &&
# $NavCommand='MoveColumnStart' )
# => MoveColStart {
# ruleRef = name(^^match);
# column = index(match);
# ::StartPosition = ^^^match[0][-1][index(match)-count(^match)].dfs;
# ::EndPosition = ^^^match[0][-1][index(match)-count(^match)].offset;
# };
# char ? ( has_parent(match, 2) && name(^match)=="mn" && name(^^match)=="msrow" &&
# $NavCommand='MoveColumnEnd' )
# => MoveColEnd {
# ruleRef = name(^^match);
# column = index(match);
# ::StartPosition = ^^^match[count(^^^match)-1][-1][index(match)-count(^match)].dfs;
# ::EndPosition = ^^^match[count(^^^match)-1][-1][index(match)-count(^match)].offset;
# };
# any ? ( (name(match)=="mn" || name(match)=="none") &&
# has_parent(match) && name(^match)=="mscarries" &&
# ($NavCommand="MovePrevious" || $NavCommand='MoveCellPrevious') && has_previous(match) )
# => MoveCell {
# ruleRef = name(^match);
# wordRef = "previous";
# ::StartPosition = previous(match).dfs;
# ::EndPosition = previous(match).offset;
# };
# # no previous child -- in first column -- don't move
# any ? ( (name(match)=="mn" || name(match)=="none") &&
# has_parent(match) && name(^match)=="mscarries" &&
# ($NavCommand="MovePrevious" || $NavCommand='MoveCellPrevious') )
# => MoveCell {
# ruleRef = name(^match);
# wordRef = "previous";
# childIndex = -1; # key to know what to say for each notation
# ::SpeakAfterMove = false;
# };
# any ? ( (name(match)=="mn" || name(match)=="none") &&
# has_parent(match) && name(^match)=="mscarries" &&
# ($NavCommand="MoveNext" || $NavCommand='MoveCellNext') && has_next(match) )
# => MoveCell {
# ruleRef = name(^match);
# wordRef = "next";
# ::StartPosition = next(match).dfs;
# ::EndPosition = next(match).offset;
# };
# # no next child -- in last column -- don't move
# any ? ( (name(match)=="mn" || name(match)=="none") &&
# has_parent(match) && name(^match)=="mscarries" &&
# ($NavCommand="MoveNext" || $NavCommand='MoveCellNext') )
# => MoveCell {
# ruleRef = name(^match);
# wordRef = "next";
# childIndex = -1; # key to know what to say for each notation
# ::SpeakAfterMove = false;
# };
# any ? ( (name(match)=="mn" || name(match)=="none") &&
# has_parent(match) && name(^match)=="mscarries" &&
# $NavCommand='MoveCellUp' && has_previous(^match) )
# => MoveCell {
# ruleRef = name(^match);
# wordRef = "up";
# ::StartPosition = ^^match[index(^match)-1][index(match)-count(^match)].dfs;
# ::EndPosition = ^^match[index(^match)-1][index(match)-count(^match)].offset;
# };
# # no previous child -- in first row -- don't move
# any ? ( (name(match)=="mn" || name(match)=="none") &&
# has_parent(match) && name(^match)=="mscarries" &&
# $NavCommand='MoveCellUp' )
# => MoveCell {
# ruleRef = name(^match);
# wordRef = "up";
# childIndex = -1; # key to know what to say for each notation
# ::SpeakAfterMove = false;
# };
# any ? ( (name(match)=="mn" || name(match)=="none") &&
# has_parent(match) && name(^match)=="mscarries" &&
# $NavCommand='MoveCellDown' && has_next(^match) )
# => MoveCell {
# ruleRef = name(^match);
# wordRef = "down";
# ::StartPosition = ^^match[index(^match)+1][-1][index(match)-count(^match)].dfs;
# ::EndPosition = ^^match[index(^match)+1][-1][index(match)-count(^match)].offset;
# };
# # no next child -- in last row -- don't move
# any ? ( (name(match)=="mn" || name(match)=="none") &&
# has_parent(match) && name(^match)=="mscarries" &&
# $NavCommand='MoveCellDown' )
# => MoveCell {
# ruleRef = name(^match);
# wordRef = "down";
# childIndex = -1; # key to know what to say for each notation
# ::SpeakAfterMove = false;
# };
# mn ? ( has_parent(match, 2) && name(^match)=="mn" && name(^^match)=="msrow" &&
# $NavCommand='MoveColumnStart' )
# => MoveColStart {
# ruleRef = name(^^match);
# column = index(match);
# ::StartPosition = ^^^match[0][-1][index(match)-count(^match)].dfs;
# ::EndPosition = ^^^match[0][-1][index(match)-count(^match)].offset;
# };
# mn ? ( has_parent(match, 2) && name(^match)=="mn" && name(^^match)=="msrow" &&
# $NavCommand='MoveColumnEnd' )
# => MoveColEnd {
# ruleRef = name(^^match);
# column = index(match);
# ::StartPosition = ^^^match[count(^^^match)-1][-1][index(match)-count(^match)].dfs;
# ::EndPosition = ^^^match[count(^^^match)-1][-1][index(match)-count(^match)].offset;
# };
- name: default-cell-move
tag: "*"
match:
- "$NavCommand='MoveCellPrevious' or $NavCommand='MoveCellNext' or"
- "$NavCommand='MoveCellUp' or $NavCommand='MoveCellDown' or"
- "$NavCommand='MoveColumnStart' or $NavCommand='MoveColumnEnd' or"
- "$NavCommand='ReadCellCurrent'"
replace:
- test:
if: "ancestor::m:mtd"
then:
- x: "ancestor::m:mtd[1]" # try again on an mtd node
else:
- t: "not in table" # phrase('not in table' or matrix)
- pause: long
- set_variables: [SpeakExpression: "'false'"]
# ======== Move/Read/Describe Next rules =================
# skip 'none'
- name: move-next-none
tag: [none, mprescripts]
match:
- "($NavCommand = 'MoveNext' or $NavCommand = 'ReadNext' or $NavCommand = 'DescribeNext' or $NavCommand = 'DescribePrevious' or $NavCommand = 'MoveNextZoom') and"
- "parent::*[1][name(.)='mmultiscripts'] and following-sibling::*"
replace:
- with:
variables: [Following: "following-sibling::*[1]"]
replace:
# two 'none's in a row -- move over and try again; one 'none', zoom in on next
- test:
if: "$Following[name(.)='none']"
then: [x: "$Following"]
else:
- with:
variables: [Move2D: "'in'", Child2D: "$Following"] # phrase('in' the denominator)
replace: [x: ".."]
- with:
variables: [NavCommand: "'MoveNextZoom'"]
replace: [x: "$Following"]
# skip 'none'
- name: move-previous-none
tag: [none, mprescripts]
match:
- "($NavCommand = 'MovePrevious' or $NavCommand = 'ReadPrevious' or $NavCommand = 'DescribePrevious' or $NavCommand = 'MovePreviousZoom') and"
- "parent::*[1][name(.)='mmultiscripts'] and preceding-sibling::*"
replace:
- with:
variables: [Preceding: "preceding-sibling::*[1]"]
replace:
# two 'none's in a row -- move over and try again; one 'none', zoom in on preceding
- test:
if: "$Preceding[name(.)='none']"
then: [x: "$Preceding"]
else:
- with:
variables: [Move2D: "'in'", Child2D: "$Preceding"] # phrase('in' the denominator)
replace: [x: ".."]
- with:
variables: [NavCommand: "'MovePreviousZoom'"]
replace: [x: "$Preceding"]
# skip invisible chars except for Enhanced mode when "times" should be read
- name: move-next-invisible
tag: "*"
match:
- "($NavCommand = 'MoveNext' or $NavCommand = 'ReadNext' or $NavCommand = 'DescribeNext') and"
- "following-sibling::*[1][name(.)='mo' and translate(., '\u2061\u2062\u2063\u2064', '')='']"
replace:
- with:
variables: [SayCommand: "string($NavVerbosity = 'Verbose')"]
replace: [x: "."]
- test:
if: "following-sibling::*[1][.='\u2062' or .='\u2064'] and
($NavMode='Enhanced' or ($NavMode='Simple' and following-sibling::*[2][not(IsNode(., 'mathml'))]))" # invisible times and plus
then: [set_variables: [NavNode: "following-sibling::*[1]/@id"]]
else: [x: "following-sibling::*[1]"]
- name: move-next-no-auto-zoom-at-edge
# at edge of 2D and in a mode where moving right isn't an option
tag: "*"
variables: [EdgeNode: "EdgeNode(., 'right', '2D')"]
match: "$NavCommand = 'MoveNext' and $NavMode!='Character' and not($AutoZoomOut) and $EdgeNode/@id!=@id"
replace:
- test:
if: "$MatchCounter = 0 and $NavVerbosity = 'Verbose'"
then:
- t: "cannot move right" # phrase('cannot move right')
- pause: medium
- with:
variables:
- Move2D: "'end of'"
- Child2D: "$EdgeNode/*[last()]"
- MatchCounter: $MatchCounter + 1
replace: [x: "$EdgeNode"]
- pause: long
- set_variables: [SpeakExpression: "'false'"]
- name: move-next-no-auto-zoom-at-edge-math
# at edge of math -- no where to go (must be after we rule out being at the edge of 2D) because we want to speak that
tag: "*"
match:
- "($NavCommand = 'MoveNext' or $NavCommand = 'ReadNext' or $NavCommand = 'DescribeNext') and"
- "(self::m:math or name(EdgeNode(., 'right', 'math'))='math')" # at edge of math
replace:
- test:
if: "$MatchCounter = 0 and $NavVerbosity = 'Verbose'"
then:
- t: "cannot" # phrase('cannot' move right in expression)
- test:
- if: "$NavCommand = 'MoveNext'"
then: [t: "move"] # phrase('move' to next entry in table)
- else_if: "$NavCommand = 'ReadNext'"
then: [t: "read"] # phrase('read' next entry in table)
else: [t: "describe"] # phrase('describe' next entry in table)
- t: "right" # phrase(move 'right')
- pause: short
- t: "end of math" # phrase(move 'end of math')
- pause: long
- set_variables: [SpeakExpression: "'false'"]
- name: move-next-auto-zoom-up-one-level
# Last child or in auto-zoom'd in-- move up a level and try again
# Note: we've already checked the for the case where we are at an edge and should not AutoZoomOut
tag: "*"
match:
- "($NavCommand = 'MoveNext' or $NavCommand = 'ReadNext' or $NavCommand = 'DescribeNext') and"
- "( not(following-sibling::*) or"
- " ( $NavMode='Enhanced' and "
- " count(following-sibling::*)=1 and (IsBracketed(.., '(', ')') or IsBracketed(.., '[', ']'))"
- " )"
- ")"
replace:
- with:
variables: [SayCommand: "string($NavVerbosity = 'Verbose')"]
replace: [x: "."]
- test:
if: "following-sibling::*"
then:
- with:
variables: [Move2D: "'in'", Child2D: "."] # phrase('in' the denominator)
replace: [x: ".."]
else:
- with:
variables: [Move2D: "'out of'", Child2D: "."]
replace: [x: ".."]
- x: ".."
# At this point, if XXXNext, then we know there is must be a right sibling
- name: move-next-default
tag: mtd
match: "$Move2D = '' and ($NavCommand = 'MoveNext' or $NavCommand = 'ReadNext' or $NavCommand = 'DescribeNext')"
replace:
- with:
variables: [SayCommand: "string($NavVerbosity = 'Verbose')"]
replace: [x: "."]
- test:
if: "following-sibling::*"
then:
- test:
if: "$NavVerbosity = 'Verbose'"
then:
- t: "column" # phrase(the previous 'column' in the table)
- x: "count(preceding-sibling::*)+2"
- pause: short
- test:
if: "$NavMode = 'Character'"
then:
- with:
variables: [NavCommand: "'MoveNextZoom'"]
replace: [x: "following-sibling::*[1]"]
else: [set_variables: [NavNode: "following-sibling::*[1]/*[1]/@id"]]
else:
- x: ".." # try again at the row level
- name: move-next-default
tag: [mtr, mlabeledtr]
match: "$Move2D = '' and
($NavCommand = 'MoveNext' or $NavCommand = 'ReadNext' or $NavCommand = 'DescribeNext') and
following-sibling::*"
replace:
- with:
variables: [SayCommand: "string($NavVerbosity = 'Verbose')"]
replace: [x: "."]
- test:
if: "$NavMode = 'Character'"
then:
- with:
variables: [NavCommand: "'MoveNextZoom'"]
replace: [x: "following-sibling::*[1]"]
else:
- set_variables: [NavNode: "following-sibling::*[1]/@id"]
- name: move-next-auto-zoom-parens
# auto-zoom into next child if next child is parenthesized expr
tag: "*"
match:
- "($NavCommand = 'MoveNext' or $NavCommand = 'ReadNext' or $NavCommand = 'DescribeNext') and"
- "$NavMode='Enhanced' and"
- "parent::m:mrow and following-sibling::* and"
- "following-sibling::*[1][self::m:mrow and count(*)=3 and " #exclude empty parens
- " (IsBracketed(., '(', ')') or IsBracketed(., '[', ']'))"
- " ]"
replace:
- with:
variables: [SayCommand: "string($NavVerbosity = 'Verbose')"]
replace: [x: "."]
- set_variables: [NavNode: "following-sibling::*[1]/*[2]/@id"]
# normal cases for MoveNext
- name: move-next-locked-zoom-level
# locked zoom level
tag: "*"
match:
- "($NavCommand = 'MoveNext' or $NavCommand = 'ReadNext' or $NavCommand = 'DescribeNext') and"
- "$ReadZoomLevel>=0"
replace:
- with:
variables: [SayCommand: "string($NavVerbosity = 'Verbose')"]
replace: [x: "."]
- test:
# if in base (nothing before), we must be moving to a script, so "in" will be said
if: "preceding-sibling::* and following-sibling::*[1][name(.)='none']"
then:
- with:
variables: [Move2D: "'out of'", Child2D: "."]
replace: [x: ".."]
- x: "following-sibling::*[1]"
else:
- with:
variables: [Move2D: "'in'", Child2D: "following-sibling::*[1]"] # phrase('in' the denominator)
replace: [x: ".."]
- with:
variables: [NavCommand: "'MoveNextZoom'"]
replace: [x: "following-sibling::*[1]"]
- name: move-next-default
tag: "*"
match: "$NavCommand = 'MoveNext' or $NavCommand = 'ReadNext' or $NavCommand = 'DescribeNext'"
replace:
- with:
variables: [SayCommand: "string($NavVerbosity = 'Verbose')"]
replace: [x: "."]
- test:
if: "following-sibling::*[1][@data-from-mathml='none' or @data-from-mathml='mprescripts']"
then: [x: "following-sibling::*[1]"]
else:
- test:
if: "IsNode(.., '2D') or not(IsNode(.., 'mathml'))"
then:
- with:
variables: [Move2D: "'in'", Child2D: "following-sibling::*[1]"] # phrase('in' the denominator)
replace: [x: ".."]
- set_variables: [NavNode: "following-sibling::*[1]/@id"]
# ======== Move/Read/Describe Previous rules =================
# skip invisible chars except for Enhanced mode when "times" should be read
- name: move-previous-invisible
tag: "*"
match:
- "($NavCommand = 'MovePrevious' or $NavCommand = 'ReadPrevious' or $NavCommand = 'DescribePrevious') and"
- "preceding-sibling::*[1][name(.)='mo' and translate(., '\u2061\u2062\u2063\u2064', '')='']"
replace:
- with:
variables: [SayCommand: "string($NavVerbosity = 'Verbose')"]
replace: [x: "."]
- test:
if: "preceding-sibling::*[1][.='\u2062' or .='\u2064'] and $NavMode='Enhanced'" # invisible times and plus
then: [set_variables: [NavNode: "preceding-sibling::*[1]/@id"]]
else: [x: "preceding-sibling::*[1]"]
# two rules for when can't move left
- name: move-previous-no-auto-zoom-at-edge
# at edge of 2D and in a mode where moving left isn't an option
tag: "*"
variables: [EdgeNode: "EdgeNode(., 'left', '2D')"]
match: "$NavCommand = 'MovePrevious' and $NavMode!='Character' and not($AutoZoomOut) and $EdgeNode/@id!=@id"
replace:
- test:
if: "$MatchCounter = 0 and $NavVerbosity = 'Verbose' and $NavCommand = 'MovePrevious'"
then:
- t: "cannot move left" # phrase('cannot move left' in expression)
- pause: medium
- with:
variables:
- Move2D: "'end of'"
- Child2D: "$EdgeNode/*[1]"
- MatchCounter: $MatchCounter + 1
replace: [x: "$EdgeNode"]
- pause: long
- name: move-previous-no-auto-zoom-at-edge-of-math
# at edge of math -- no where to go (must be after we rule out being at the edge of 2D) because we want to speak that
tag: "*"
match:
- "($NavCommand = 'MovePrevious' or $NavCommand = 'ReadPrevious' or $NavCommand = 'DescribePrevious') and"
- "(self::m:math or name(EdgeNode(., 'left', 'math'))='math')"
replace:
- t: "start of math" # phrase('start of math')
- pause: long
- set_variables: [SpeakExpression: "'false'"]
- name: move-previous-at-end
tag: "*"
match:
- "($NavCommand = 'MovePrevious' or $NavCommand = 'ReadPrevious' or $NavCommand = 'DescribePrevious') and"
- "name(EdgeNode(., 'left', 'math'))='math'" # at edge of math
replace:
- test:
if: "$MatchCounter = 0 and $NavVerbosity = 'Verbose'"
then:
- t: "cannot move left" # phrase('cannot move left')
- pause: short
- with:
variables: [Move2D: "'start of'", Child2D: "."]
replace: [x: "."]
- pause: long
- set_variables: [SpeakExpression: "'false'"]
- name: move-previous-auto-zoom-up-one-level
# Last child or in auto-zoom'd in-- move up a level and try again
# Note: we've already checked the for the case where we are at an edge and should not AutoZoomOut
tag: "*"
match:
- "($NavCommand = 'MovePrevious' or $NavCommand = 'ReadPrevious' or $NavCommand = 'DescribePrevious') and"
- "( not(preceding-sibling::*) or"
- " ( $NavMode='Enhanced' and "
- " count(preceding-sibling::*)=1 and (IsBracketed(.., '(', ')') or IsBracketed(.., '[', ']'))"
- " )"
- ")"
replace:
- with:
variables: [SayCommand: "string($NavVerbosity = 'Verbose')"]
replace: [x: "."]
- test:
if: "preceding-sibling::*"
then:
- with:
variables: [Move2D: "'in'", Child2D: "."] # phrase('in' the denominator)
replace: [x: ".."]
else:
- with:
variables: [Move2D: "'out of'", Child2D: "."]
replace: [x: ".."]
- x: ".."
- name: move-previous-auto-zoom-parens
# auto-zoom into previous child if previous child is parenthesized expr
# Note: there is an asymmetry here from MoveNext because the base of a scripted might have parens for grouping, but not true for the script
tag: "*"
match:
- "($NavCommand = 'MovePrevious' or $NavCommand = 'ReadPrevious' or $NavCommand = 'DescribePrevious') and"
- "$NavMode='Enhanced' and"
- "preceding-sibling::* and"
- "(parent::m:mrow or parent::m:msub or parent::m:msup or"
- " (count(preceding-sibling::*)=1 and (parent::m:msubsup or parent::m:mmultiscripts))" # make sure moving into base
- ") and"
- "preceding-sibling::*[1][self::m:mrow and count(*)=3 and " #exclude empty parens
- " (IsBracketed(., '(', ')') or IsBracketed(., '[', ']'))"
- " ]"
replace:
- with:
variables: [SayCommand: "string($NavVerbosity = 'Verbose')"]
replace: [x: "."]
- test:
if: "not(parent::m:mrow)"
then:
- with:
variables: [Move2D: "'in'", Child2D: "preceding-sibling::*[1]"] # phrase('in' the denominator)
replace: [x: ".."]
- set_variables: [NavNode: "preceding-sibling::*[1]/*[2]/@id"]
# normal cases for MovePrevious
- name: move-previous-default
tag: mtd
match: "$Move2D = '' and
($NavCommand = 'MovePrevious' or $NavCommand = 'ReadPrevious' or $NavCommand = 'DescribePrevious') and
preceding-sibling::*"
replace:
- with:
variables: [SayCommand: "string($NavVerbosity = 'Verbose')"]
replace: [x: "."]
- test:
if: "$NavVerbosity = 'Verbose'"
then:
- t: "column" # phrase(the first 'column' in the table)
- x: "count(preceding-sibling::*)"
- pause: short
- test:
if: "$NavMode = 'Character'"
then:
- with:
variables: [NavCommand: "'MovePreviousZoom'"]
replace: [x: "preceding-sibling::*[1]"]
else: [set_variables: [NavNode: "preceding-sibling::*[1]/*[last()]/@id"]]
- name: move-previous-default
tag: [mtr, mlabeledtr]
match: "$Move2D = '' and ($NavCommand = 'MovePrevious' or $NavCommand = 'ReadPrevious' or $NavCommand = 'DescribePrevious')"
replace:
- with:
variables: [SayCommand: "string($NavVerbosity = 'Verbose')"]
replace: [x: "."]
- test:
if: "preceding-sibling::*"
then:
- test:
if: "$NavMode = 'Character'"
then:
- with:
variables: [NavCommand: "'MovePreviousZoom'"]
replace: [x: "preceding-sibling::*[1]"]
else:
- set_variables: [NavNode: "preceding-sibling::*[1]/@id"]
else: [x: ".."] # try again for after
- name: move-previous-locked-zoom-level
# locked zoom level
tag: "*"
match:
- "($NavCommand = 'MovePrevious' or $NavCommand = 'ReadPrevious' or $NavCommand = 'DescribePrevious') and"
- "$ReadZoomLevel>=0"
replace:
- with:
variables: [SayCommand: "string($NavVerbosity = 'Verbose')"]
replace: [x: "."]
- test:
# if moving into base (nothing before), we must be moving to the base, so "in" will be said
if: "count(preceding-sibling::*) > 2 and preceding-sibling::*[1][name(.)='none']"
then:
- with:
variables: [Move2D: "'out of'", Child2D: "."]
replace: [x: ".."]
- x: "preceding-sibling::*[1]" # skip over 'none'
else:
- with:
variables: [Move2D: "'in'", Child2D: "preceding-sibling::*[1]"] # phrase('in' the denominator)
replace: [x: ".."]
- with:
variables: [NavCommand: "'MovePreviousZoom'"]
replace: [x: "preceding-sibling::*[1]"]
- name: move-previous-default
tag: "*"
match: "$NavCommand = 'MovePrevious' or $NavCommand = 'ReadPrevious' or $NavCommand = 'DescribePrevious'"
replace:
- with:
variables: [SayCommand: "string($NavVerbosity = 'Verbose')"]
replace: [x: "."]
- test:
if: "preceding-sibling::*[1][@data-from-mathml='none' or @data-from-mathml='mprescripts']"
then: [x: "preceding-sibling::*[1]"]
else:
- test:
if: "IsNode(.., '2D') or not(IsNode(.., 'mathml'))"
then:
- with:
variables: [Move2D: "'in'", Child2D: "preceding-sibling::*[1]"] # phrase('in' the denominator)
replace: [x: ".."]
- set_variables: [NavNode: "preceding-sibling::*[1]/@id"]
# ********* ReadZoomLevel toggle ***************
# These set ::NavMode
- name: toggle-mode-up
tag: "*"
match: "$NavCommand = 'ToggleZoomLockUp'"
replace:
- test:
- if: "$NavMode = 'Enhanced'"
then:
- t: "character" # phrase(a mathematical 'character')
- set_variables: [NavMode: "'Character'", ReadZoomLevel: "1"]
- else_if: "$NavMode = 'Character'"
then:
- t: "simple" # phrase(a 'simple' way to do something)
- set_variables: [NavMode: "'Simple'", ReadZoomLevel: "1"]
- else:
- t: "enhanced" # phrase(an 'enhanced' way to do something)
- set_variables: [NavMode: "'Enhanced'", ReadZoomLevel: "-1"]
- t: "mode" # phrase(a simple 'mode' of use)
- pause: long
- test:
- if: "$NavMode != 'Enhanced'" # potentially need to zoom to the sibling
then:
- with:
variables: [MatchCounter: "$MatchCounter + 1", NavCommand: "'MoveNextZoom'"]
replace: [x: "."]
- name: toggle-mode-down
tag: "*"
match: "$NavCommand = 'ToggleZoomLockDown'"
replace:
- test:
- if: "$NavMode = 'Enhanced'"
then:
- t: "simple" # phrase(an 'simple' way to do something)
- set_variables: [NavMode: "'Simple'", ReadZoomLevel: "1"]
- else_if: "$NavMode = 'Character'"
then:
- t: "enhanced" # phrase(an 'enhanced' way to do something)
- set_variables: [NavMode: "'Enhanced'", ReadZoomLevel: "-1"]
- else:
- t: "character" # phrase(a mathematical 'character')
- set_variables: [NavMode: "'Character'", ReadZoomLevel: "1"]
- t: "mode" # phrase(a simple 'mode' of use)
- pause: long
- test:
- if: "$NavMode != 'Enhanced'" # potentially need to zoom to the sibling
then:
- with:
variables: [MatchCounter: "$MatchCounter + 1", NavCommand: "'MoveNextZoom'"]
replace: [x: "."]
- name: toggle-speech-describe
tag: "*"
match: "$NavCommand = 'ToggleSpeakMode'"
replace:
- test:
if: "$Overview = 'true'"
then:
- t: "speak expression after move" # phrase('speak expression after move')
- pause: long
- set_variables: [Overview: "'false'"]
else:
- t: "overview of expression after move" # phrase('overview of expression after move')
- pause: long
- set_variables: [Overview: "'true'"]
- name: current
tag: "*"
match: "$NavCommand = 'ReadCurrent' or $NavCommand = 'DescribeCurrent'"
replace:
- test:
if: "$NavVerbosity = 'Verbose'"
then:
- test:
- if: "$NavCommand = 'ReadCurrent'"
then: [t: "read"] # phrase('read' next entry in table)
else: [t: "describe"] # phrase('describe' next entry in table)
- t: "current" # phrase('current' entry in table)
- pause: long
- set_variables: [NavNode: "@id"]
# this needs to be near the end because we only test for 'Describe', "Read", etc., and we don't want to get 'DescribeNext', etc.
- name: placemarker
tag: "*"
match:
- "starts-with($NavCommand, 'Read') or "
- "starts-with($NavCommand, 'Describe') or "
- "starts-with($NavCommand, 'MoveTo')"
replace:
- test:
if: "$NavVerbosity != 'Terse'"
then:
- test:
- if: "starts-with($NavCommand, 'Read')"
then: [t: "read"] # phrase('read' next entry in table)
- else_if: "starts-with($NavCommand, 'Describe')"
then: [t: "describe"] # phrase('describe' next entry in table)
- else_if: "starts-with($NavCommand, 'MoveTo')"
then: [t: "move to "] # phrase('move to' the next entry in table)
else: [t: "set"] # phrase('set' the value of the next entry in table)
- t: "placeholder" # phrase('placeholder' for the value)
- x: "$PlaceMarkerIndex"
- pause: long
- set_variables: [NavNode: "$PlaceMarker"]
- name: set-placemarker
tag: "*"
match: "starts-with($NavCommand, 'SetPlacemarker')"
replace:
- test:
if: "$NavVerbosity != 'Terse'"
then:
- t: "set placeholder" # phrase('set placeholder' to the value)
- x: "$PlaceMarkerIndex"
- pause: long
- set_variables: [NavNode: "@id"]
# ********* WhereAmI ***************
# FIX: WhereAmI needs support from the Rust code to loop around and do speech at each iteration.
# Alternatively, it could insert a special token that Rust code does a "replace" on with the speech (e.g. SPEECH_AT{id})
# or a new command "speak" which takes a node id
- name: where-am-i-start
tag: "*"
match: "($NavCommand = 'WhereAmI' or $NavCommand = 'WhereAmIAll') and $MatchCounter = 0"
replace:
- translate: "@id"
- pause: long
- with:
variables: [MatchCounter: "$MatchCounter + 1"]
replace: [x: ".."]
- name: where-am-i-stop
tag: "*"
match:
- "($NavCommand = 'WhereAmI' or $NavCommand = 'WhereAmIAll') and $MatchCounter > 0 and"
# stopping conditions
- "(self::m:math or "
- " parent::*[self::m:math and (count(*)=1 or (count(*)=2 and *[2][.=',' or .='.' or .=';' or .='?']) ) ] "
- ")"
replace:
- test:
if: "$NavCommand = 'WhereAmI'"
then:
- t: "inside of nothing more" # phrase('inside of nothing more')
- pause: long
- set_variables: [SpeakExpression: "'false'"]
else:
- t: "inside" # phrase('inside' a big expression)
- pause: medium
- set_variables: [NavNode: "@id"]
- name: where-am-i-middle
tag: "*"
match: "$NavCommand = 'WhereAmI' or $NavCommand = 'WhereAmIAll'"
replace:
- t: "inside" # phrase('inside' a big expression)
- pause: medium
- test:
- if: "$NavMode='Enhanced' and parent::*[self::m:mrow and (IsBracketed(., '(', ')', false) or IsBracketed(., '[', ']', false))]"
then: [x: ".."] # auto-zoom up
- else_if: "$NavCommand = 'WhereAmI'"
then: [set_variables: [NavNode: "@id"]]
else:
- translate: "@id"
- pause: long
- x: '..'