Accessibility/WebAccessibilityAPI: Difference between revisions

 
(47 intermediate revisions by the same user not shown)
Line 28: Line 28:


   readonly attribute AttributeMap attributes;
   readonly attribute AttributeMap attributes;
  readonly attribute sequence<DOMString> patterns;
   readonly attribute Object toPattern(DOMString type);
   readonly attribute Object toPattern(DOMString type);


Line 157: Line 159:
<code>
<code>
AttributeSet .''hasAnyOf''
AttributeSet .''hasAnyOf''
::Return true if any of the given object properties and its non null value matches a { name, value } pair from the map. If a null value property value matches a map name then value is ignored and is not part of the match.
::Return true if any of the object properties matches to the object attributes.


AttributeSet .''hasAllOf''
AttributeSet .''hasAllOf''
::Return true if all properties of the given object and their values match name/value pairs from the map. If matched property value is null then value is ignored and is not part of the match.
::Return true if all of the object properties matches to the object attributes.
</code>
</code>
Each property of the object is a string or an array of strings. If array of strings is given then the object attribute is expected to have any of given values, empty array means the attribute value doesn't matter for match. String value and null values are treated as single element array or empty array correspondingly.
<b>Example #1.</b>
<pre>
var attrs = {
  live: [ "assertive", "polite" ],
  relevant: "show",
  busy: null
};
var matched = accEl.hasAllOf(attrs);
// Matches if the ccessible element has "live" object attribute of
// "assertive" or "polite" values, has "relevant" object attribute of
// "show" value, and it has "busy" attribute.
</pre>




Example of a script processing list autocomplete.
<b>Example #2. Process autocomplete.</b>


<pre>
<pre>
var accElm = document.getElementById("foo").accessibleElement;
var accEl = document.getElementById("foo").a11ement;
var autocomplete = accElm.attributes.get("autocomplete");
 
var checkObj = { autocomplete: [ "list", "both" ] };
if (accEl.attributes.hasAllOf(checkObj)) {
  doAutocomplete();
}
 
// Alternatively you can do
var autocomplete = accEl.attributes.get("autocomplete");
if (["list", "both"].indexOf(autocomplete) != -1) {
if (["list", "both"].indexOf(autocomplete) != -1) {
   processListAutocomplete();
   doAutocomplete();
}
}
</pre>
</pre>
Line 188: Line 214:
::Lists all notifications that qualifies for live region.
::Lists all notifications that qualifies for live region.
</code>
</code>


===Patterns===
===Patterns===
Line 194: Line 221:


<code>
<code>
AccessibleElement .''patterns''
::Returns a list of all patterns supported by the accessible element.
Object .''toPattern''(DOMString type)
Object .''toPattern''(DOMString type)
::Returns an object for the pattern of given type if supported by an accessible element.
::Returns an object for the pattern of given type if supported by an accessible element.
Line 199: Line 229:


See [[#Patters|patterns section]] for details.
See [[#Patters|patterns section]] for details.


===Relations===
===Relations===
Line 264: Line 293:
<code>
<code>
AccessibleElement .''actions''
AccessibleElement .''actions''
::Returns a [[#ActionSet|ActionSet]] object of actions exposed by the accessible element. The returned object is not live, i.e. it is not updated if the accessible element actions change.
::Returns a [[#ActionMap|ActionMap]] object of actions exposed by the accessible element. The returned object is not live, i.e. it is not updated if the accessible element actions change.
</code>
</code>


Line 345: Line 374:




<div id="ActionSet">
====ActionMap====
 
====ActionSet====
</div>


Accessible actions are presented by <code>ActionSet</code> map like object of pairs { action name, action object }.
Accessible actions are presented by <code>ActionMap</code> map like object of pairs { action name, action object }.


<pre>
<pre>
interface ActionSet {
interface ActionMap {
   readonly maplike<DOMString, Action>;
   readonly maplike<DOMString, Action>;
};
};
Line 448: Line 474:
                           });
                           });
</pre>
</pre>
====Questions/concerns====
* Interactions should be extended to allow to specify a control that triggers the action. The concept is described by InidieUI: you can use mouse, touchscreen, keyboard, voice control or a control element to invoke the action. Maybe Interactions should be renamed to Triggers to share terms with IndieUI.
Here's the image of InideUI action/interaction concept.
<div style="height: 254px; overflow: hidden;">
http://www.w3.org/WAI/intro/iui-scroll.png
</div>
Alternatively we could introduce relations between actions like "triggers" and "triggeredby".
* What about highlevel actions (uberactions), for example, "press" buttons means "send something somewhere". This may be covered by label. Another example, selected listitem on the left changes a view on the right, "select" actions has meaning of "change view". Or checkbox that enables/disables related controls, "check/uncheck" action means "enable/disable controls". These examples correlates to controlledby/controllerfor relations, but the relations doesn't reveal type of control.


===Parent-child relations===
===Parent-child relations===
Line 532: Line 573:
   AccessiblePos? move(DOMNode container, long offset);
   AccessiblePos? move(DOMNode container, long offset);
   AccessiblePos? move(DOMPoint);
   AccessiblePos? move(DOMPoint);
  AccessiblePos? move(Where where, Matcher);


   AccessiblePos? search(Where where, Matcher);
  AccessiblePos? move(Where where, Criteria);
   AccessiblePos? search(Where where, Criteria);


   readonly attribute AccessibleElement root;
   readonly attribute AccessibleElement root;
   readonly attribute AccessibleElement? anchor;
   readonly attribute AccessibleElement? anchor;
   readonly attribute Offset offset;
   readonly attribute Offset offset;
 
   DOMRangeBound toDOM();
   long compare(AccessiblePos pos);
 
};
};


Line 549: Line 588:


====Construction====
====Construction====
Construction by accessible element and offset relative it.
<code>
<code>
AccessiblePos .''Constructor''(AccessibleElement, Offset, AccessibleElement)
AccessiblePos .''Constructor''(AccessibleElement, Offset, AccessibleElement)
Line 561: Line 603:
</code>
</code>


The offset may be either a number, a numeric mapping of caret position in the content, or a literal.


<pre>
<pre>
typedef long or OffsetLiterals Offset;
typedef long or OffsetLiterals Offset;
enum OffsetLiterals {
enum OffsetLiterals {
   "beforebegin",
   "before",
   "afterbegin",
   "begin",
   "at",
   "at",
   "beforeend",
   "end",
   "afterend"
   "after"
};
};
</pre>
</pre>


<code>
<code>
OffsetLiterals .''beforebegin''
OffsetLiterals .''before''
::Used to set the accessible position right before the accessible element beginning
::Used to set the accessible position right before the accessible element beginning.
</code>
</code>


<code>
<code>
OffsetLiterals .''afterbegin''
OffsetLiterals .''begin''
::Used to set the accessible position right after the accessible element beginning
::Used to set the accessible position right after the accessible element beginning.
</code>
</code>


<code>
<code>
OffsetLiterals .''at''
OffsetLiterals .''at''
::Used to set the accessible position at the accessible element
::Used to set the accessible position at the accessible element.
</code>
</code>


<code>
<code>
OffsetLiterals .''beforeend''
OffsetLiterals .''end''
::Used to set the accessible position right before the accessible element ending
::Used to set the accessible position right before the accessible element ending.
</code>
</code>


<code>
<code>
OffsetLiterals .''afterend''
OffsetLiterals .''after''
::Used to set the accessible position right after the accessible element ending
::Used to set the accessible position right after the accessible element ending.
</code>
</code>




<code>
<b>Example #1. Input widget.</b>
AccessiblePos .''Constructor''(DOMPoint, AccessibleElement)
 
::Constructs the accessible position.
<pre>
::Parameters
<input id="input" value="Hello">
:::''point'' of ''DOMPoint''
<script>
::::a point, the accessible position should be set at
var input = document.getElementById("input").a11ement;
:::''root'' of ''AccessibleElement'', optional
 
::::a root of subtree containing the position. If not provided then parent document is used.
// Position is at the control
</code>
var p = new A11ePos(input, "at");
 
// Position is in the control text at 0 offset.
p = new A11ePos(input, "begin");


// Position is in the control text at 5 offset.
p = new A11ePos(input, "end");
</script>
</pre>


<code>
<b>Example #2. Image inside a paragraph.</b>
AccessiblePos .''Constructor''(DOMNode, long, AccessibleElement)
::Constructs the accessible position from (DOMNode, offset) pair.
::Parameters
:::''node'' of ''DOMNode''
::::the container node
:::''offset'' of ''long''
::::offset within the container node
:::''root'' of ''AccessibleElement'', optional
::::a root of subtree containing the position. If not provided then parent document is used.
</code>


<pre>
<p id="p">I <img id="img" src="love.png" alt="love"> you</p>


<code>
<script>
AccessiblePos .''Constructor''
var img = document.getElementById("img").a11ement;
::Constructs the accessible position equal to given position.
 
::Parameters
// The position is right before the image, at 2 offset relative the paragraph.
:::''pos'' of ''AccessiblePos''
var p = new A11ePos(img, "before");
::::accessible position to copy
 
</code>
// The position is right after the image, at 3 offset relative the paragraph.
p = new A11ePos(img, "after");
 
// The position is at the image, no offset relative the paragraph is applicable.
p = new A11ePos(img, "at");
p = new A11ePos(img, "begin");
p = new A11ePos(img, "end");
</script>
</pre>


<b>Example #3. Table.</b>


Examples:
<pre>
<pre>
var pos1 = new A11ePos(new DOMPoint(x, y), document.accessibleElement);
<table id="table">
  <tr>
    <td>cell</td>
  </tr>
</table>
<script>
var table = document.getElementById("table").a11ement;


var pos2 = new A11ePos(button.accessibleElement, "at");
// The position at the table.
var pos3 = new A11ePos(paragraph.accessibleElement, "inside start");
var p = new A11ePos(table, "at");


var pos4 = new A11ePos(pos2);
// The position is in the table, before the row.
var p = new A11ePos(table, "begin");
 
// The position is in the table, after the row.
var p = new A11ePos(table, "end");
 
// The position is in the table, before the row, numeric offset is ignored.
var p = new A11ePos(table, 1);
</script>
</pre>
</pre>




====Move through the content====
Construction from a point on the screen. See [[#Hit_testing|hit testing]] for details.


<code>
<code>
AccessiblePos .''move''(AccessilbeElement, Offset)
AccessiblePos .''Constructor''(DOMPoint, AccessibleElement)
::Move the accessible position to the given anchor and offset.
::Constructs the accessible position.
::Parameters
::Parameters
:::''element'' of ''AccessibleElement''
:::''point'' of ''DOMPoint''
::::the anchor
::::a point, the accessible position should be set at
:::''offset'' of ''Offsest'', optional
:::''root'' of ''AccessibleElement'', optional
::::offset relative the anchor
::::a root of subtree containing the position. If not provided then parent document is used.
:::Return itself.
</code>
 


Construction from a DOM node and offset relative it. See [[#Caret_and_selection|selection]] section for examples.


AccessiblePos .''move''(DOMNode, long)
<code>
::Move the accessible position to the given DOM node at given offset.
AccessiblePos .''Constructor''(DOMNode, long, AccessibleElement)
::Constructs the accessible position from (DOMNode, offset) pair.
::Parameters
::Parameters
:::''node'' of ''DOMNode''
:::''node'' of ''DOMNode''
::::the anchor
::::the container node
:::''offset'' of ''long''
:::''offset'' of ''long''
::::offset relative the anchor
::::offset within the container node
:::Return itself.
:::''root'' of ''AccessibleElement'', optional
::::a root of subtree containing the position. If not provided then parent document is used.
</code>
 


Copy constructor.


AccessiblePos .''move''(DOMPoint)
<code>
::Move the accessible position in the content. Returns true if succeeded.
AccessiblePos .''Constructor''
::Constructs the accessible position equal to given position.
::Parameters
::Parameters
:::''point'' of ''DOMPoint''
:::''pos'' of ''AccessiblePos''
::::the point the accessible position should be moved to.
::::accessible position to copy
:::Return itself.
</code>




AccessiblePos .''move''(Where, Matcher)
====Change the position====
::Move the accessible position to the content. Returns true if succeeded.
::Parameters
:::''where'' of ''Where''
::::where the search should be performed
:::''matcher'' of ''Matcher''
::::function describing a match
:::Return itself.


There is a bunch of methods to change accessible position.


AccessiblePos .''search''(Where, Matcher)
<code>
::Finds the accessible position relative the current one. Returns new instance.
AccessiblePos .''move''(AccessilbeElement, Offset)
::Move the accessible position to the given anchor and offset.
::Parameters
::Parameters
:::''where'' of ''Where''
:::''element'' of ''AccessibleElement''
::::where the search should be performed
::::the anchor
:::''matcher'' of ''Matcher''
:::''offset'' of ''Offsest'', optional
::::function describing a match
::::offset relative the anchor
:::Return new instance.
:::Return itself or null if not succeeded.
</code>




<pre>
AccessiblePos .''move''(DOMNode, long)
enum Where { "forward",
::Move the accessible position to the given DOM node at given offset.
            "backward",
::Parameters
            "cyclic forward",
:::''node'' of ''DOMNode''
            "cyclic backward",
::::the anchor
            "tofirst",
:::''offset'' of ''long''
            "tolast",
::::offset relative the anchor
            "left",
:::Return itself or null if not succeeded.
            "right",
            "up",
            "down",
            "above",
            "under",
            "closest"
};
</pre>


<code>
Where .''forward''
::Search after the accessible position.


Where .''backward''
AccessiblePos .''move''(DOMPoint)
::Search backwards from the accessible position.
::Move the accessible position in the content. Returns true if succeeded.
::Parameters
:::''point'' of ''DOMPoint''
::::the point the accessible position should be moved to.
:::Return itself or null if not succeeded.


Where .''cyclic forward''
::Search forwards from the accessible position in cycle.


Where .''cyclic backward''
====Move through the content====
::Search backwards from the accessible position in cycle.


Where .''tofirst''
Accessible position can be moved through the content by criteria.
::Search for a first match inside the root.


Where .''tolast''
AccessiblePos .''move'' (''Where'', ''Criteria'')
::Search backwards (from last to first element) for a first match inside the root.
::Move the accessible position to the content complying with criteria.
::Parameters
:::''where'' of ''Where''
::::where the search should be performed
:::''criteria'' of ''Criteria''
::::function describing a match
:::Return itself if succeeded, otherwise null.


Where .''left''
::Search for a match left to the position.


Where .''right''
AccessiblePos .''search'' (''Where'', ''Criteria'')
::Search for a match right to the position.
::Search for content of the given criteria relative the accessible position.
::Parameters
:::''where'' of ''Where''
::::where the search should be performed
:::''criteria'' of ''Criteria''
::::function describing a match
:::Return new instance if succeeded, otherwise null.
</code>


Where .''up''
::Search for a match up from the position.


Where .''down''
=====Where to search=====
::Search for a match down from the position.


Where .''above''
The search area and the way accessible elements are traversed are defined by <code>Where</code> argument. It defines whether traversal is perfromed by accessible tree hierarchy or by layout on the screen, and whether the traversal is relative of the current position or relative of the root.
::Search for a match above the position.


Where .''under''
<pre>
::Search for a match under the position.
enum Where {
  "forward",
  "backward",
  "cyclic forward",
  "cyclic backward",
  "tofirst",
  "tolast",


Where .''closest''
  "left",
::Search for a geometrically closest match to the position.
  "right",
</code>
  "up",
  "down",
  "above",
  "under",
  "closest"
};
</pre>


<code>
Where .''forward''
::Search after the accessible position.


Examples:
Where .''backward''
<pre>
::Search backwards from the accessible position.
function matcher(elm) { }
var pos1 = new A11ePos(new DOMPoint(x, y), a11edoc).move("forward", matcher);


var pos2 = new A11ePos(elm, "at").move("backward", matcher);
Where .''cyclic forward''
::Search forwards from the accessible position in cycle.


var pos3 = new A11ePos(pos).move("forward", matcher);
Where .''cyclic backward''
or
::Search backwards from the accessible position in cycle.
var pos2 = pos2.search("forward", matcher);
</pre>


Where .''first''
::Search for a first match inside the root.


Where .''last''
::Search backwards (from last to first element) for a first match inside the root.


=====Matching function=====
Where .''left''
::Search for a match left to the position.


<pre>
Where .''right''
callback Matcher = SearchTerm sequence<SearchTerm> (AccessibleElement);
::Search for a match right to the position.
</pre>


Where .''up''
::Search for a match up from the position.


<code>
Where .''down''
Matcher
::Search for a match down from the position.
::A matching function used to define an algorithm of how the position is moved through the document.
::Returns
:::a search term(s)
</code>


Where .''above''
::Search for a match above the position.
Where .''under''
::Search for a match under the position.
Where .''closest''
::Search for a geometrically closest match to the position.
</code>
<b>Example.</b>
<pre>
function criteria(el) { }
var pos1 = new A11ePos(new DOMPoint(x, y), a11edoc).move("forward", criteria);
var pos2 = new A11ePos(elm, "at").move("backward", criteria);
var pos3 = new A11ePos(pos).move("forward", criteria);
or
var pos2 = pos2.search("forward", criteria);
</pre>
=====Criteria=====
Criteria can be either a literal describing how the position should be moved or a matching function called for every traversed accessible.


<pre>
<pre>
enum SearchTerm {
typedef CriteriaLiteral or CriteriaFunc Criteria;
</pre>
 
 
<pre>
enum CriteriaLiteral {
   "char",
   "char",
   "word",
   "word",
Line 791: Line 902:
   "paragraph",
   "paragraph",
   "change",
   "change",
   "bit",
   "bit"
 
  "at",
  "skipsubtree",
  "stop",
  "next"
};
};
</pre>
</pre>


<code>
<code>
SearchTerm .''char''
CriteriaLiteral .''char''
::Used to move the position one char long.
::Used to move the position one char long.
</code>


<code>
CriteriaLiteral .''word''
SearchTerm .''word''
::Used to move the position to the next word.
::Used to move the position to the next word.
</code>


<code>
CriteriaLiteral .''sentence''
SearchTerm .''sentence''
::Used to move the position to the next sentence.
::Used to move the position to the next sentence.
</code>


<code>
CriteriaLiteral .''line''
SearchTerm .''line''
::Used to move the position to beginning of next line or end of previous line.
::Used to move the position to beginning of next line or end of previous line.
</code>


<code>
CriteriaLiteral .''paragraph''
SearchTerm .''paragraph''
::Used to move the position to beginning of next/previous paragraph.
::Used to move the position to beginning of next/previous paragraph.
</code>


<code>
CriteriaLiteral .''change''
SearchTerm .''change''
::Used to move the position to next change either of text attribute or new accessible.
::Used to move the position to next change either of text attribute or new accessible.
</code>


<code>
CriteriaLiteral .''bit''
SearchTerm .''bit''
::Used to move the position to next/previous navigable position. For example, from accessible start inside of it to right before accessible outside of it or moving from end of this line to start of next line in case of soft line break lines.
::Used to move the position to next/previous navigable position. For example, from accessible start inside of it to right before accessible outside of it or moving from end of this line to start of next line in case of soft line break lines.
</code>
</code>




<code>
<b>Example #1. Traverse a paragraph by words.</b>
SearchTerm .''at''
 
::Used to set the position at the element. Search is stopped.
<pre>
var p = document.getElementById("p").a11ement;
var pos1 = new A11ePos(p, "begin", p), pos2 = null;
while (pos2 = pos1.search("forward", "word")) {
  console.log(pos2.text(pos1));
  pos1 = pos2;
}
</pre>


SearchTerm .''stop''
<pre>
::Search is discontinued after other search terms are processed.
<p id="p">Mozilla is a <a href="">free-software</a> community which
produces the <a href="">Firefox web browser</a>.</p>
</pre>


SearchTerm .''skipsubtree''
The script above generates the log "Mozilla ", "is ", "a ", "free-", "software ", "community", "which ", "produces ", "the ", "Firefox ", "web ", "browser."
::Subtree of traversed element is ignored for next step.


SearchTerm .''next''
<pre>
::Used to continue the search if other search terms weren't succeed. Default option.
<p id="p">Mo<a href="">zilla</a>.</p>
</code>
</pre>


The log is "Mozilla".


<b>Examples.</b>


<pre>
<pre>
var pos1 = new A11ePos(document.getElementById("a").a11ement);
callback CriteriaFunc = CriteriaLiteral or Offset or "next" (AccessibleElement);
var pos2 = pos1.search("forward", () = > {return [ "word", "stop" ];});
var pos3 = pos1.search("forward", () => {return "word"; });
</pre>
</pre>


<pre>
<code>
<p>Click <a id="a">here</a>, fellow</p>
CriteriaFunc
</pre>
::A criteria function used to define an algorithm of the match.
pos2 and pos3 points after "here" word in the link.
::Returns
:::a criteria term or offset(s)
</code>


<pre>
If "next" is returned then criteria function is reentered with next traversed accessible. If offset is given then traversing is stopped, the accessible position is moved to the traversed accessible element and given offset. If criteria literal is returned then the accessible position is moved to the position complying the criteria. If it cannot be moved withing current accessible then it reenters with next accessible.
<p>Click <a id="a"><img src=""></a>, fellow</p>
</pre>
pos2 equals to pos1, pos3 points after "fellow" word in the link.




<b> Example. Navigate by widgets and structures, and by words in case of text.</b>
<b> Example. Navigate by widgets and structures, and by words in case of text.</b>
<pre>
<pre>
function matcher(aElm)
function criteria(aEl)
{
{
   var role = document.taxonOf("role", aElm.role);
   var role = document.taxonOf("role", aEl.role);
   if (role.is("widget")
   if (role.is("widget")
     return [ "at", "skipsubtree" ];
     return "at";


   if (role.is("structure")
   if (role.is("structure")
     return "at";
     return "at";


   // Reenters with next accessible if it cannot move to the next word within this accessible.
   // Reenters with next accessible if it cannot move to the next line within this accessible.
   if (role.is("textcontainer"))
   if (role.is("textcontainer"))
     return "word";
     return "line";


   return "next";
   return "next";
}
}


pos.move("next", finder);
pos.move("next", criteria);
</pre>
</pre>


====Other methods====
====Other methods====


<code>
<code>
AccessiblePos .''root''
::Returns the accessible element the position movement is restricted to.
AccessiblePos .''anchor''
AccessiblePos .''anchor''
::Returns the accessible element the position is contained by. If the position is at element then element itself.
::Returns the accessible element the position is at or contained by.
</code>


<code>
AccessiblePos .''offset''
AccessiblePos .''compare''
::Return an offset of the accessible position, either numeric or text.
::Returns -1 if the given position is before in accessible tree than this position, 0 if they are the same, 1 if the given position is after.
 
AccessiblePos .''toDOM''
::Returns DOMRangeBound object which is a pair of DOM node and content offset relative it.
</code>
</code>


===Questions/concerns===
<pre>
 
dictionary DOMRangeBound {
* Do we need *is* method right on AccessibleElement or should we have Role interface having that method or should AccessibleElement return role taxa as a role.
  DOMNode node;
* Do we need to have "inContextOf" on AccessibleElement to check what the accessilbe belongs to. Note, native implementation doing some cache may be faster than tree traversal. If we need it then it's worth to consider *is* method too.
  long offset;
* Do we need isAnyOf() method additionally?
};
* Yura: we should have async API for search.
</pre>
 


===Virtual cursor===
===Virtual cursor===
Line 923: Line 1,026:
Whenever the virtual cursor position is changed then ''cursor_moved'' event is fired.
Whenever the virtual cursor position is changed then ''cursor_moved'' event is fired.


Questions/concerns:
===Questions/concerns===
 
* Do we need *is* method right on AccessibleElement or should we have Role interface having that method or should AccessibleElement return role taxa as a role.
* Do we need to have "inContextOf" on AccessibleElement to check what the accessilbe belongs to. Note, native implementation doing some cache may be faster than tree traversal. If we need it then it's worth to consider *is* method too.
* Do we need isAnyOf() method additionally?
* Do we need to compare() method to compare two positions
* Ensure that the virtual cursor is survivable. If it ends up in the hidden sub-tree for example. If subtee gets destroyed then cursor should be moved. If AccessiblePos doesn't have matching function then it's unclear where it should be moved.
* Ensure that the virtual cursor is survivable. If it ends up in the hidden sub-tree for example. If subtee gets destroyed then cursor should be moved. If AccessiblePos doesn't have matching function then it's unclear where it should be moved.
* Also take into account walking to/from parent/child frames
* Also take into account walking to/from parent/child frames
* Should it be outlined as moved?
* Yura: we should have async API for search.


==Text==
[ Promise variant if we had async API].


<pre>
<pre>
partial interface AccessiblePos {
function getText() {
   DOMString textInBetween(AccessiblePos pos);
  var p = new A11ePos().move("first", a => a.role == "paragraph");
   readonly attribute AttributeSet textAttributes;
  var startPos;
};
  return p.then(function(pos) {
</pre>
    startPos = pos;
    return pos.search("forward", a => "line"); }).
  then(function(endPos) {
    return startPos.textInBetween(endPos);
  };
}
getText().then(function(text) { /* Say text */ })
</pre>
 
==Text==
 
<pre>
partial interface AccessiblePos {
   DOMString text(AccessiblePos pos);
  DOMString text(CriteriaLiteral criteria);
   readonly attribute AttributeSet textAttributes;
};
</pre>


<code>
<code>
AccessibleDocument .''textInBetween''
AccessibleDocument .''text(AccessiblePos)''
::Returns the text enclosed between this and given accessible positions.
::Returns the text enclosed between this and given accessible positions.
AccessibleDocument .''text(CriteriaLiteral)''
::Returns the text at the position complying the given criteria. See [[#Criteria|criteria]] for options.
</code>
</code>


Example how to get first line of the first encountered paragraph:
Example how to get first line of the first encountered paragraph:


<pre>
<pre>
var startPos = new A11ePos().move("first", a => a.role == "paragraph");
function criteria(a)
var endPos = startPos && startPos.search("forward", a => [ "line", "stop" ]);
  { return a.role == "paragraph" ? "being" : "next" };
var text = startPos.textInBeetween(startPos, endPos);
var startPos = new A11ePos(document).move("first", criteria);
 
var endPos = startPos && startPos.search("forward", "line");
say(text);
var text = startPos.text(startPos, endPos);
</pre>
</pre>


[ Promise variant if we had async API].
or same effect by using criteria literal


<pre>
<pre>
function getText() {
function criteria(a)
  var p = new A11ePos().move("first", a => a.role == "paragraph");
  { return a.role == "paragraph" ? "being" : "next" };
  var startPos;
var pos = new A11ePos(document).move("first", criteria);
  return p.then(function(pos) {
var text = pos.text("line");
    startPos = pos;
    return pos.search("forward", a => "line"); }).
  then(function(endPos) {
    return startPos.textInBetween(endPos);
  };
}
getText().then(function(text) { /* Say text */ })
</pre>
</pre>


Line 994: Line 1,118:
p.textAttributes.get("font-weight") == 700; // true
p.textAttributes.get("font-weight") == 700; // true
</pre>
</pre>


==Caret and selection==
==Caret and selection==
Line 1,017: Line 1,140:
AccessibleDocument .''selectionStart''
AccessibleDocument .''selectionStart''
::Get/set selection start.
::Get/set selection start.


AccessibleDocument .''selectionEnd''
AccessibleDocument .''selectionEnd''
Line 1,024: Line 1,146:




<b>Example</b>
<b>Example.</b>


<pre>
<pre>
Line 1,031: Line 1,153:
var pos2 = new A11ePos(pos.anchor.nextSibling);
var pos2 = new A11ePos(pos.anchor.nextSibling);
document.a11ement.selectionEnd = pos;
document.a11ement.selectionEnd = pos;
</pre>
<b>Example #2. Connection with DOM selection.</b>
<pre>
var sel = window.getSelection();
var pos1 = new A11ePos(sel.anchorNode, sel.anchorOffset);
var pos2 = new A11ePos(sel.focusNode, sel.focusOffset);
var p1 = pos1.toDOM();
sel.getRangeAt(0).setStart(p1.node, p1.offset);
var p2 = pos2.toDOM();
sel.getRangeAt(0).setEnd(p2.node, p2.offset);
</pre>
</pre>


==Geometry==
==Geometry==


Outline can be drawn around a range defined by two positions.
The section contains bunch of methods and approaches to deal with web page geometry.
 
===Outline===
 
AT can outline a position or a range enclosed between two positions. This feature is useful to track stuff like virtual cursor.
<pre>
<pre>
partial interface AccessibleDocument {
partial interface AccessibleDocument {
   void outline(AccessiblePos pos1, AccessiblePos pos2);
   void outline(AccessiblePos pos1, optional AccessiblePos pos2);
   void clearOutlines();
   void clearOutlines();
};
};
</pre>
</pre>


<code>
<code>
AccessibleDocument .''outline''
AccessibleDocument .''outline''
::Outlines a range bounded by two positions.
::Outlines a position if second position is omitted. If second position is omitted then outlines a collapsed range.


AccessibleDocument .''clearOutlines''
AccessibleDocument .''clearOutlines''
Line 1,052: Line 1,190:
</code>
</code>


===Geometrical navigation===


AT can scan the web page by moving the position geometrically up/down/left/right.
<pre>
<pre>
partial interface AccessiblePos {
var scanner
   readonly attribute DOMPoint coordinate;
{
  readonly attribute unsigned long distanceBetween(AccessiblePos pos2);
   start: function() {
};
    if (this.stopped)
</pre>
      return;


<code>
    var nextpos = this.pos.move("right", this.controller);
AccessiblePos .''coordinate''
    if (!nextpos)
::Returns a DOM point for top left corner of the accessible position.
      nextpos = this.pos.move("down", this.controller);
</code>


<code>
    if (nextpos) {
AccessiblePos .''distanceBetween''
      document.outline(nextpos);
::Returns a distance between this and given accessible position.
      window.setTimeout(this.start.bing(this), 1000);
</code>
    }
  },
  stop: function() {
    this.stopped = true;
  },


  controller: function(aEl) {
    var role = document.a11ement.taxonOf("role", aElm.role);
    if (role.is("widget"))
      return "at";


Questions/concerns
    return "next";
*It's tricky how to style that but maybe document or accessible should decide how it should be styled, for example, black outline on black background.
  }
  pos: new A11ePos(new DOMPoint(0, 0)),
  stopped: false
}
</pre>


===Hit testing===
===Hit testing===
Line 1,096: Line 1,247:
</pre>
</pre>


[
#Hit testing on touch pads needs to find closest accessible in some local area. We may have strict (current implementation of modern APIs) and not strict modes. Mouse may need to work in not strict mode if operated by person with motor disability.


#Also it would need a history of movement (previous coordinates) to make right decision. If you move from up to down then you want to pick up next element vs previous one (geometry part).
Questions/concerns
 
*Outline: it's tricky to style that but maybe document or accessible should decide how it should be styled, for example, black outline on black background.
#We might need to have scanning feature, from left/right/up/down to right/left/down/up and semantic scanning from start to end. (geometry part)
*Do we need a way to return coordinates of accessible position's edge points?
].
*Do we need a method to calculate distance between two positions?
*Do we need a method to calculate boundaries (aka containing rect)?
*Do we need a method to check whether the given point is contained by a range?


==Events==
==Events==
Line 1,250: Line 1,401:




<div id="JSSource">
==Multiprocessing==
 
Accessible tree is scoped to a process, i.e. you can have seamless accessible tree between all document frames until it violates security policy. In case of multiprcess frames you have to use standard mechanism to communicate between processes.
 
When the virtual cursor reaches a frame start or end and it was attempted to move it further then message is sent to a process where the virtual cursor would go in case of single process.
 
Questions/concerns
* what messaging mechanism should be used
* should it be allowed to move from iframe to parent document?
 
 
<div id="JSSource">


==Make the content accessible==
==Make the content accessible==
Line 1,344: Line 1,506:
control.relations.add("labelledby", label.a11yment);
control.relations.add("labelledby", label.a11yment);
</pre>
</pre>
[to do actions/interactions and patterns]




Line 1,382: Line 1,541:
   readonly attribute object attributes;
   readonly attribute object attributes;
   readonly attribute object relations;
   readonly attribute object relations;
  readonly attribute sequence<DOMString> patterns;
  object toPattern(DOMString);


   readonly attribute DOMString text;
   readonly attribute DOMString text;
   object textAttributesAt(offset);
   object textAttributesAt(Offset);


   readonly attribute object actions;
   readonly attribute object actions;
Line 1,502: Line 1,664:


In this case the <code>labelledby</code> relation getter is more powerful than its ARIA version and all computations are running iff somebody inquired the relation.
In this case the <code>labelledby</code> relation getter is more powerful than its ARIA version and all computations are running iff somebody inquired the relation.
====Patterns====
<code>
AccessibleSource .''patterns''
::Returns a list of all patterns implemented by the accessible element.
</code>
<code>
AccessibleSource .''toPattern''
::Returns an object for the given pattern. If the method is not provided then the source object has to implemented itself all claimed patterns.
</code>
<b>Example #4. Implement patterns.</b>
<pre>
var slider = {
  role: "slider",
  patterns: [ "value" ],
  toPattern: function(aName) {
    return aName == "value" ? this : null;
  },
  min: 0,
  max: 100,
  value: 0
};
</pre>


====Text====
====Text====


<code>
<code>
AccessibleTextSource .''text''
AccessibleSource .''text''
::Return the text.
::Return the text.


AccessibleTextSource .''textAttributesAt''
AccessibleSource .''textAttributesAt''
::Returns object of { name: value } pairs representing text attributes at given offset within the accessible element text.
::Returns object of { name: value } pairs representing text attributes at given offset within the accessible element text.
</code>
</code>




<b> Example #4. Text content.</b>
<b> Example #5. Text content.</b>


While text should be normally in the content, there are cases when the author has to provide it for non-text content. For example, this technique can be used to turn HTML image into text.
While text should be normally in the content, there are cases when the author has to provide it for non-text content. For example, this technique can be used to turn HTML image into text.
Line 1,552: Line 1,745:




<b>Example #5. Describe actions and interactions.</b>
<b>Example #6. Describe actions and interactions.</b>


<pre>
<pre>
Line 1,571: Line 1,764:
In case if interactions cannot be provided then the accessible source have to implement <code>activate</code> method to invoke actions.
In case if interactions cannot be provided then the accessible source have to implement <code>activate</code> method to invoke actions.


====Proto====
====Inheritance====


<code>
<code>
AccessibleSource .''proto''
AccessibleSource .''element''
::Point to accessible element implementation as it was before 'source' set. Set by the browser when the accessible source is attached to the accessible element.  
::Refers to the accessible element implementation as there were no accessible source attached to it. Set by the browser when the accessible source is attached to the accessible element.
</code>
</code>




===Change the accessible tree===
<b>Example. Lazy extension of accessible relations.</b>


The web developer can alter existing accessible tree including subtree creations and deletions. This can be done either by <code>AccessibleSource</code> object assigned to the accessible element or by direct manipulations on the accessible tree.
<pre>
var source = {
  get relations() {
    var r = this.element.relations;
    r.add("labelledby", document.querySelector(this.selector));
    return r;
  },
  element: null,
  selector: ""
};
document.getElementById("button").accessibleSource = source;
</pre>
 
===Change the accessible tree===
 
The web developer can alter existing accessible tree including subtree creations and deletions. This can be done either by <code>AccessibleSource</code> object assigned to the accessible element or by direct manipulations on the accessible tree.


====Direct tree alteration====
====Direct tree alteration====
Line 1,693: Line 1,901:
   onactivated: function(aEvent) {
   onactivated: function(aEvent) {
     if (aEvent.region == "button")
     if (aEvent.region == "button")
       this.proto.set("focused");
       this.element.set("focused");
     else
     else
       this.proto.states.unset("focused");
       this.element.states.unset("focused");
   }
   }
   proto: null
   element: null // set by the browser
};
};


Line 1,706: Line 1,914:
===Feedback notifications===
===Feedback notifications===


An accessible element may be notified of any kind of event, including its accessible source property change. In case of notification the browser will fire accessible events and update its cache if necessary. Decision when to notify the browser or not should be based on accessible events model. In other words if the property change in native markup causes an accessible event then same change in accessible source requires it too.
Accessible element may be notified of any kind of event, including about property change when managed by connected source object. When notified the browser may fire accessible events and update its cache if necessary. Decision whether to notify the browser or not should be based on the accessible events model. In other words if a property change in native markup causes accessible event then it's quite likely the event is expected for similar accessible source change.


<pre>
<pre>
Line 1,719: Line 1,927:
::Called when source has been changed to notify the host accessible element about property change. The browser is responsible to fire proper accessible events and update its internal representation.
::Called when source has been changed to notify the host accessible element about property change. The browser is responsible to fire proper accessible events and update its internal representation.
::Parameters
::Parameters
:::prop
:::eventType of DOMString
::::Name of the property like ''role'' or ''state''
::::The event type to fire.
:::value
:::attrs
::::Value describing the event.
::::Attributes describing the event. See [[#Event_types|event types]] for attributes variety.
</code>
</code>


Line 1,741: Line 1,949:
   }
   }
};
};
function onfocus(aEvent) {
  // Change accessible properties when element is focused.
  // The change requires accessible events.
  aEvent.target.accessibleSource = listboxSource;
}
</pre>
</pre>


===Implied semantics===
===Implied semantics===


The author has to be less verbose as possible. If a role usually implies number of sets or attributes then they are supposed to set so the authur doesn't need to list them. For example if <code>listitem</code> is selectable then you don't have to point <code>selectable</code> state on it, in other words, these code snippets are equivalent:
The idea is to help the author to keep the code less verbose as possible. If role implies a number of preset attributes or states then they are present on the accessible element by default, i.e. if the author didn't list them. For example, if <code>listitem</code> role is selectable and focusable by default then the author doesn't have to point <code>selectable</code> and <code>focusable</code> states when he describes a listitem source, in other words, these code snippets are equivalent:


<pre>
<pre>
var source = {
var source1 = {
   role: "listitem"
   role: "listitem"
};
};
</pre>


<pre>
var source2 = {
var source = {
   role: "listitem",
   role: "listitem",
   states: [ "selectable" ]
   states: [ "selectable", "focusable" ]
};
};
</pre>
</pre>


The implied semantics is described by [[#SemanticsProviders|semantics provides]].
Implied semantics is described by [[#SemanticsProviders|taxonomies]].
 


===Conflicts===
===Conflicts===


[to be done conflicts between native semantics, ARIA and this API]
[to be done conflicts between native semantics, ARIA and this API, I tend to think that the author should be able to replace/remove native semantics if needed. This can ruin the web page but it gives the absolute control over the page content. A dangerous but quite powerful tool like atomic energy. ]
 


===Sniffing===
===Sniffing===


In order to make optimization the content provider has to know whether accessibility consumer is active. The provider can add a callback for <code>deploy</code> and <code>conceal</code> event types which will be triggered when consumer appears/gets inactive.  
In order to make an optimization, the content provider has to know whether an accessibility consumer (like screen reader) is active. The provider can add a callback for <code>deploy</code> and <code>conceal</code> event types which will be triggered when consumer appears/gets inactive.  


<pre>
<pre>
Line 1,926: Line 2,139:
   }
   }
};
};
document.accessibleElement.import("role", taxa);
document.a11ement.import("role", taxa);
</pre>
</pre>


<pre>
<pre>
var taxon = document.accessibleElement.taxonOf("role", "checklistitem");
var taxon = document.a11ement.taxonOf("role", "checklistitem");
if (taxon.is("menuitem")) {
if (taxon.is("menuitem")) {
   // process menuitem selection
   // process menuitem selection
Line 1,939: Line 2,152:


<pre>
<pre>
var taxon = document.accessibleDocument.taxonOf("role", "main");
var taxon = document.a11ement.taxonOf("role", "main");
var isLandmark = taxon.attributes.has("landmark");
var isLandmark = taxon.attributes.has("landmark");
</pre>
</pre>
Line 1,946: Line 2,159:


<pre>
<pre>
var taxon = document.accessibleDocument.taxonOf("role", "listbox");
var taxon = document.a11ement.taxonOf("role", "listbox");
print(listboxRole.relations.get("states"))); // prints "focusable"
print(listboxRole.relations.get("states"))); // prints "focusable"
</pre>
</pre>
Line 1,996: Line 2,209:


<div id="TaxonomyTypes">
<div id="TaxonomyTypes">
===Taxonomy types===
===Taxonomy types===
</div>
</div>
Each taxonomy is described means of its own interface which is used to define the hierarchy and interconnections between taxonomies. If taxon refers to taxa of other taxonomy type then a string describing the reference may be in form of "taxon:modifier", where taxon is a name of related taxon and modifier is an extra information about how the taxa are interconnected. For example, role taxon may be connected to "live" taxon of attributes taxonomy by placing "live:assertive" value in <code>attributes</code> field which means that the role supports "live" object attribute and its default value is "assertive" on it.


====Roles====
====Roles====


<pre>
<pre>
dictionary RoleTaxa {
dictionary RoleTaxon {
   DOMString landmark;
   DOMString landmark;
   DOMString description;
   DOMString description;


   DOMString or sequence<DOMString> parents;
   sequence<DOMString> parents;
   DOMString or sequence<DOMString> owns;
   sequence<DOMString> owns;
   DOMString or sequence<DOMString> states;
   sequence<DOMString> states;
   Object attributes;
   Object attributes;
  sequence<DOMString> relations;
  sequence<DOMString> actions;
};
};
</pre>
</pre>
Line 2,015: Line 2,233:


<code>
<code>
RoleTaxa .''landmark''
RoleTaxon .''landmark''
::Navigation landmark name if applicable.
::Navigation landmark name if applicable.


RoleTaxa .''description''
RoleTaxon .''description''
::Localized role description.
::Localized role description.


RoleTaxa .''parents''
RoleTaxon .''parents''
::List of roles the role is inherited from.
::List of roles the role is inherited from.


RoleTaxa .''owns''
RoleTaxon .''owns''
::List of roles allowed in children of the role. Used for validation.
::List of roles allowed in children of the role. Used for validation.


RoleTaxa .''states''
RoleTaxon .''states''
::List of states allowed on the role.
::List of states allowed on the role. Optional modifier is "default" which points out that the state is exposed on the role until explicitly specified otherwise. See [[#Implied_semantics|implied semantics]].
 
RoleTaxon .''attributes''
::List of attributes supported by the role. Default value of the attribute may be specified as modifier. For example, "live:polite" points out that "live" object attribute has "polite" value by default. If default value is not specified then it's taken from referred attribute taxon description.
 
RoleTaxon .''relations''
::List of relations supported by the role.


RoleTaxa .''attributes''
RoleTaxon ..''actions''
::List of attributes maintained by the role.
::List of supported actions. Actions from the list are exposed on the accessible element depending on states present on it. For example, if supported actions are "check" and "uncheck", then "check" is exposed if the accessible element doesn't have "checked" state, otherwise "unchecked".
</code>
</code>


====States====
 
<b>Example. ARIA <code>textbox</code> role.</b>


<pre>
<pre>
dictionary StateTaxa {
var taxa = {
}
  widget: { }
  input: {
    parents: [ "widget" ],
    states: [ "focusable:default", "focused" ]
  },
  textbox: {
    description: "text field",
    parents: [ "input" ],
    states: [
      "singleline:default", "multiline",
      "editable", "readonly",
      "required"
    ],
    attributes: [ "autocomplete" ]
  }
};
</pre>
</pre>


=HTML and beyond=


This doc introduces common patterns to express the semantics of markup languages to accessibility. Markup specifics is not a target for this doc in general. Each markup specification has to take care to describe their accessibility stuff in terms of this API.
<b>Example. ARIA <code>checkbox</code> role.</b>


=Extensibility=
<pre>
var taxa = {
  checkbox: {
    description: "checkbox",
    parents: [ "input" ],
    states: [ "checkable:default, "checked" ],
    actions: [ "check", "uncheck" ]
  }
};
</pre>


The web application might need to extend default taxonomies to express the new semantics. For example, the web service publishing music sheets can introduce new characteristics like role, states, etc to describe music sheet content. However the web application should take care to explain new characteristic by extending default taxonomies, i.e. by describing the connection between old and new characteristics. That will resolve any backward compatibility issue, so if the consumer doesn't know about new roles then he can still figure out a closest match it's aware about. For example, if the web app author introduces "x-redbutton' and provides a role taxonomy for it saying this is an extension of 'button' role, then the consumer unfamiliar with 'x-redbutton' role will treat it as a button.


The author should follow name convention to avoid potential collisions with future additions into the spec predefined lists. Thus all names should be prefixed by 'x-' like 'x-redbutton' from example above.
<b>Example. ARIA <code>log</code> role.</b>


==Music sheet example==
<pre>
var taxa = {
  region: {
  },
  log: {
    parents: [ "region" ],
    attributes: [ "live:polite" ]
  }
};
</pre>


To make a music sheet accessible the web app may introduce bunch of new roles, attributes and relations:
====States====


<pre>
<pre>
roles:
dictionary StateTaxon {
   'sheet' - inherited from 'section'
   DOMString description;
   'note' - inherited from 'image', describes the note
   sequence<DOMString> dependents;
  DOMString exclusives;
}
</pre>
</pre>


<pre>
role 'sheet' attributes:
  instrument: DOMString,
  tempo: number/DOMString
  clef: DOMString
</pre>


<pre>
<code>
role 'note' attributes:
StateTaxon .''description''
   key: enum { C, D, E, F, G, A, H },
::Localized taxa description.
   alteration: enum { none, flat, sharp },
 
   octave: enum { ... },
StateTaxon .''dependents''
   duration: number,
::List of all dependent states. For example, "focused" state always accompanied by "focusable" state.
   effects: sequence<DOMString>, // tremolo, bend
 
StateTaxon ..''exclusives''
::Mutually exclusive states if applicable. For example, if "vertical" state is applied then "horizontal" is not and vice versa.
</code>
 
 
<b>Example.</b>
 
<pre>
var taxa = {
   focusable: {
    dependents: [ "focused" ]
  },
  focused: { },
 
  singleline: {
    exlcusives: [ "multiline" ]
  },
  multiline: {
    exlcusives: [ "singleline" ]
  },
 
  readonly: { },
   editable: { },
   required: { },
 
   checked: {
    exlusives: [ "mixed" ]
  },
   mixed: {
    exlusives: [ "checked" ]
  }
};
</pre>
</pre>
====Attributes====


<pre>
<pre>
role 'note' relations:
interface AttributeTaxon {
   crescendo: [note, ...] a list a notes the crescendo is applied to
  DOMString description;
  sequence<DOMString> values;
  DOMString default;
};
</pre>
 
 
<code>
AttributeTaxon .''description''
::Localized description of the taxon
 
AttributeTaxon .''values''
::List of all possible values of the attrbiute
 
AttributeTaxon .''default''
::Default attribute value. Takes a place if role taxa pointing to it doesn't have own default value of it.
</code>
 
 
<b>Example</b>
 
<pre>
var taxa = {
  autocomplete: {
    values: [ "none", "list", "inline", "both" ],
    default: "none"
  },
  live: {
    values: [ "none", "polite", "assertive" ],
    default: "none"
  }
};
</pre>
 
 
====Actions====
 
<pre>
dictionary ActionTaxon {
  DOMString description;
  DOMString dual;
  sequence<DOMString> states;
};
</pre>
 
<code>
ActionTaxon .''description''
::Localized action description.
 
ActionTaxon .''dual''
::Dual action taxon. When action is invoked, it is switched to its dual action if applicable.
 
ActionTaxon .''states''
::Implied states. When action is invoked, states of dual action are cleared, this action states are set.
</code>
 
 
<b>Example.</b>
 
<pre>
var taxa = {
  check: {
    description: "check",
    dual: "uncheck",
    states: [ "checked" ]
  },
  uncheck: {
    description: "uncheck",
    dual: "check"
  }
};
</pre>
 
=HTML and beyond=
 
This doc introduces common patterns to express the semantics of markup languages to accessibility. Markup specifics is not a target for this doc in general. Each markup specification has to take care to describe their accessibility stuff in terms of this API.
 
=Extensibility=
 
The web application might need to extend default taxonomies to express the new semantics. For example, the web service publishing music sheets can introduce new characteristics like role, states, etc to describe music sheet content. However the web application should take care to explain new characteristic by extending default taxonomies, i.e. by describing the connection between old and new characteristics. That will resolve any backward compatibility issue, so if the consumer doesn't know about new roles then he can still figure out a closest match it's aware about. For example, if the web app author introduces "x-redbutton' and provides a role taxonomy for it saying this is an extension of 'button' role, then the consumer unfamiliar with 'x-redbutton' role will treat it as a button.
 
The author should follow name convention to avoid potential collisions with future additions into the spec predefined lists. Thus all names should be prefixed by 'x-' like 'x-redbutton' from example above.
 
==Music sheet example==
 
To make a music sheet accessible the web app may introduce bunch of new roles, attributes and relations:
 
<pre>
roles:
  'sheet' - inherited from 'section'
  'note' - inherited from 'image', describes the note
</pre>
 
<pre>
role 'sheet' attributes:
  instrument: DOMString,
  tempo: number/DOMString
  clef: DOMString
</pre>
 
<pre>
role 'note' attributes:
  key: enum { C, D, E, F, G, A, H },
  alteration: enum { none, flat, sharp },
  octave: enum { ... },
  duration: number,
  effects: sequence<DOMString>, // tremolo, bend
</pre>
 
<pre>
role 'note' relations:
   crescendo: [note, ...] a list a notes the crescendo is applied to
   diminuendo: [note, ...] a list a notes the diminuendo is applied to
   diminuendo: [note, ...] a list a notes the diminuendo is applied to
</pre>
Or in terms of taxonomies:
<pre>
document.import("role", {
  sheet: {
    description: "sheet",
    attributes: [ "instrument", "tempo", "clef" ]
  },
  note: {
    description: "note",
    attributes: [ "key", "alteration", "octave", "duration", "effects" ],
    relations: [ "crescendo", "diminuendo" ]
  }
});
document.import("attributes", {
  instrument: {
    description: "instrument type"
  },
  tempo: {
    description: "tempo"
  },
  clef: {
    description: "clef"
  },
  key: {
    description: "key",
    values: [ "C", "D", "E", "F", "G", "A", "H" ],
  },
  alteration: {
    description: "alteration",
    values: [ "none", "flat", "sharp" ],
    default: "none"
  },
  octave: {
    description: "octave",
    values: [ "contra", "great", "small", "1line", "2line" ],
  },
  duration: {
    description: "duration"
  },
  effects: {
    description: "effects"
  }
});
document.import("relations", {
  crescendo: {
    description: "crescendo"
  },
  diminuendo: {
    description: "diminuendo"
  }
});
</pre>
</pre>
Confirmed users
1,396

edits