1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 /** @fileoverview Logic for panning a braille display within a line of braille
6 * content that might not fit on a single display.
9 goog
.provide('cvox.PanStrategy');
14 * A stateful class that keeps track of the current 'viewport' of a braille
15 * display in a line of content.
17 cvox
.PanStrategy = function() {
22 this.displaySize_
= 0;
27 this.contentLength_
= 0;
29 * Points before which it is desirable to break content if it doesn't fit
31 * @type {!Array<number>}
34 this.breakPoints_
= [];
36 * @type {!cvox.PanStrategy.Range}
39 this.viewPort_
= {start
: 0, end
: 0};
43 * A range used to represent the viewport with inclusive start and xclusive
45 * @typedef {{start: number, end: number}}
47 cvox
.PanStrategy
.Range
;
49 cvox
.PanStrategy
.prototype = {
51 * Gets the current viewport which is never larger than the current
52 * display size and whose end points are always within the limits of
53 * the current content.
54 * @type {!cvox.PanStrategy.Range}
57 return this.viewPort_
;
61 * Sets the display size. This call may update the viewport.
62 * @param {number} size the new display size, or {@code 0} if no display is
65 setDisplaySize: function(size
) {
66 this.displaySize_
= size
;
67 this.panToPosition_(this.viewPort_
.start
);
71 * Sets the current content that panning should happen within. This call may
72 * change the viewport.
73 * @param {!ArrayBuffer} translatedContent The new content.
74 * @param {number} targetPosition Target position. The viewport is changed
75 * to overlap this position.
77 setContent: function(translatedContent
, targetPosition
) {
78 this.breakPoints_
= this.calculateBreakPoints_(translatedContent
);
79 this.contentLength_
= translatedContent
.byteLength
;
80 this.panToPosition_(targetPosition
);
84 * If possible, changes the viewport to a part of the line that follows
85 * the current viewport.
86 * @return {boolean} {@code true} if the viewport was changed.
89 var newStart
= this.viewPort_
.end
;
91 if (newStart
+ this.displaySize_
< this.contentLength_
) {
92 newEnd
= this.extendRight_(newStart
);
94 newEnd
= this.contentLength_
;
96 if (newEnd
> newStart
) {
97 this.viewPort_
= {start
: newStart
, end
: newEnd
};
104 * If possible, changes the viewport to a part of the line that precedes
105 * the current viewport.
106 * @return {boolean} {@code true} if the viewport was changed.
108 previous: function() {
109 if (this.viewPort_
.start
> 0) {
110 var newStart
, newEnd
;
111 if (this.viewPort_
.start
<= this.displaySize_
) {
113 newEnd
= this.extendRight_(newStart
);
115 newEnd
= this.viewPort_
.start
;
116 var limit
= newEnd
- this.displaySize_
;
119 while (pos
< this.breakPoints_
.length
&&
120 this.breakPoints_
[pos
] < limit
) {
123 if (pos
< this.breakPoints_
.length
&&
124 this.breakPoints_
[pos
] < newEnd
) {
125 newStart
= this.breakPoints_
[pos
];
128 if (newStart
< newEnd
) {
129 this.viewPort_
= {start
: newStart
, end
: newEnd
};
137 * Finds the end position for a new viewport start position, considering
138 * current breakpoints as well as display size and content length.
139 * @param {number} from Start of the region to extend.
143 extendRight_: function(from) {
144 var limit
= Math
.min(from + this.displaySize_
, this.contentLength_
);
147 while (pos
< this.breakPoints_
.length
&& this.breakPoints_
[pos
] <= from) {
150 while (pos
< this.breakPoints_
.length
&& this.breakPoints_
[pos
] <= limit
) {
151 result
= this.breakPoints_
[pos
];
158 * Overridden by subclasses to provide breakpoints given translated
159 * braille cell content.
160 * @param {!ArrayBuffer} content New display content.
161 * @return {!Array<number>} The points before which it is desirable to break
162 * content if needed or the empty array if no points are more desirable
166 calculateBreakPoints_: function(content
) {return [];},
169 * Moves the viewport so that it overlaps a target position without taking
170 * the current viewport position into consideration.
171 * @param {number} position Target position.
173 panToPosition_: function(position
) {
174 if (this.displaySize_
> 0) {
175 this.viewPort_
= {start
: 0, end
: 0};
176 while (this.next() && this.viewPort_
.end
<= position
) {
180 this.viewPort_
= {start
: position
, end
: position
};
186 * A pan strategy that fits as much content on the display as possible, that
187 * is, it doesn't do any wrapping.
189 * @extends {cvox.PanStrategy}
191 cvox
.FixedPanStrategy
= cvox
.PanStrategy
;
193 * A pan strategy that tries to wrap 'words' when breaking content.
194 * A 'word' in this context is just a chunk of non-blank braille cells
195 * delimited by blank cells.
197 * @extends {cvox.PanStrategy}
199 cvox
.WrappingPanStrategy = function() {
200 cvox
.PanStrategy
.call(this);
203 cvox
.WrappingPanStrategy
.prototype = {
204 __proto__
: cvox
.PanStrategy
.prototype,
207 calculateBreakPoints_: function(content
) {
208 var view
= new Uint8Array(content
);
209 var newContentLength
= view
.length
;
211 var lastCellWasBlank
= false;
212 for (var pos
= 0; pos
< view
.length
; ++pos
) {
213 if (lastCellWasBlank
&& view
[pos
] != 0) {
216 lastCellWasBlank
= (view
[pos
] == 0);