Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(287)

Side by Side Diff: chrome/browser/resources/chromeos/chromevox/braille/pan_strategy.js

Issue 2496823002: Implement word wrapping and panning in multiline Braille. (Closed)
Patch Set: removed unnecessary files Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 /** @fileoverview Logic for panning a braille display within a line of braille 5 /** @fileoverview Logic for panning a braille display within a line of braille
6 * content that might not fit on a single display. 6 * content that might not fit on a single display.
7 */ 7 */
8 8
9 goog.provide('cvox.PanStrategy'); 9 goog.provide('cvox.PanStrategy');
10 10
11 /** 11 /**
12 * @constructor 12 * @constructor
13 * 13 *
14 * A stateful class that keeps track of the current 'viewport' of a braille 14 * A stateful class that keeps track of the current 'viewport' of a braille
15 * display in a line of content. 15 * display in a line of content.
16 */ 16 */
17 cvox.PanStrategy = function() { 17 cvox.PanStrategy = function() {
18 /** 18 /**
19 * @type {number} 19 * @type {number}
20 * @private 20 * @private
21 */ 21 */
22 this.displaySize_ = 0; 22 this.rowCount_ = 0;
23 /** 23 /**
24 * @type {number} 24 * @type {number}
25 * @private 25 * @private
26 */ 26 */
27 this.contentLength_ = 0; 27 this.columnCount_ = 0;
28 /** 28 /**
29 * Points before which it is desirable to break content if it doesn't fit 29 * Start and end are both inclusive.
30 * on the display.
31 * @type {!Array<number>}
32 * @private
33 */
34 this.breakPoints_ = [];
35 /**
36 * @type {!cvox.PanStrategy.Range} 30 * @type {!cvox.PanStrategy.Range}
37 * @private 31 * @private
38 */ 32 */
39 this.viewPort_ = {start: 0, end: 0}; 33 this.viewPort_ = {start: 0, end: 0};
34
35 this.wrappedBuffer_ = new ArrayBuffer(0);
36
37 this.fixedBuffer_ = new ArrayBuffer(0);
38
39 this.wrappedBrailleToText_ = [];
40
41 this.fixedBrailleToText_ = [];
42
43 this.panStrategyWrapped_ = false;
44
45 this.fixedLineCount_ = 0;
46
47 this.wrappedLineCount_ = 0;
David Tseng 2016/11/14 21:05:08 All of the members here need JS doc. Use the other
ultimatedbz 2016/11/15 00:01:17 Thanks! I actually wasn't sure how to type check.
40 }; 48 };
41 49
42 /** 50 /**
43 * A range used to represent the viewport with inclusive start and xclusive 51 * A range used to represent the viewport with inclusive start and xclusive
44 * end position. 52 * end position.
45 * @typedef {{start: number, end: number}} 53 * @typedef {{start: number, end: number}}
46 */ 54 */
47 cvox.PanStrategy.Range; 55 cvox.PanStrategy.Range;
48 56
49 cvox.PanStrategy.prototype = { 57 cvox.PanStrategy.prototype = {
50 /** 58 /**
51 * Gets the current viewport which is never larger than the current 59 * Gets the current viewport which is never larger than the current
52 * display size and whose end points are always within the limits of 60 * display size and whose end points are always within the limits of
53 * the current content. 61 * the current content.
54 * @type {!cvox.PanStrategy.Range} 62 * @type {!cvox.PanStrategy.Range}
55 */ 63 */
56 get viewPort() { 64 get viewPort() {
57 return this.viewPort_; 65 return this.viewPort_;
58 }, 66 },
59 67
60 /** 68 /**
69 * @return {Array<number>} The offset of braille and text indices of the
70 * current slice.
71 */
72 get offsetsForSlices() {
73 return [this.viewPort_.start * this.columnCount_,
74 this.brailleToText[this.viewPort_.start * this.columnCount_]];
75 },
76
77 /**
78 * @return {Array<number>} The map of Braille cells to the first index of the
79 * corresponding text character.
80 */
81 get brailleToText() {
82 if (this.panStrategyWrapped_)
83 return this.wrappedBrailleToText_;
84 else
85 return this.fixedBrailleToText_;
86 },
87
88 /**
89 * @return {ArrayBuffer} Buffer of the slice of braille cells within the
90 * bounds of the viewport.
91 */
92 getCurrentBrailleSlice: function() {
93 var buf = this.panStrategyWrapped_ ?
94 this.wrappedBuffer_ : this.fixedBuffer_;
95 buf = buf.slice(this.viewPort.start * this.columnCount_,
96 (this.viewPort.end + 1) * this.columnCount_);
97 return buf;
98 },
99
100 /**
101 * @return {string} String of the slice of text letters corresponding with
102 * the current braille slice.
103 */
104 getCurrentTextSlice: function() {
105 var brailleToText = this.brailleToText;
106 // Index of last braille character in slice.
107 var index = (this.viewPort.end + 1) * this.columnCount_ - 1;
108 // Last text character that is in slice.
109 var end = brailleToText[index];
110 // Increment index until brailleToText[index] points to a different char.
111 while (index < brailleToText.length && brailleToText[index] == end) {
112 index++;
113 }
114 return this.textBuffer.substring(
115 brailleToText[this.viewPort.start * this.columnCount_],
116 brailleToText[index]);
117 },
118
119 /**
120 * Sets the current pan strategy and resets the viewport.
121 */
122 setPanStrategy: function(wordWrap) {
123 this.panStrategyWrapped_ = wordWrap;
124 this.viewPort_.start = 0;
125 this.viewPort_.end = this.rowCount_ - 1;
126 },
127
128 /**
61 * Sets the display size. This call may update the viewport. 129 * 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 130 * @param {number} rowCount the new row size, or {@code 0} if no display is
63 * present. 131 * present.
132 * @param {number} columnCount the new column size, or {@code 0}
133 * if no display is present.
64 */ 134 */
65 setDisplaySize: function(size) { 135 setDisplaySize: function(rowCount, columnCount) {
66 this.displaySize_ = size; 136 this.rowCount_ = rowCount;
67 this.panToPosition_(this.viewPort_.start); 137 this.columnCount_ = columnCount;
138 // this.setContent(this.textBuffer, this.fixedBuffer_,
139 // this.fixedBrailleToText_);
140 // TODO do I need to set the content again?
68 }, 141 },
69 142
70 /** 143 /**
71 * Sets the current content that panning should happen within. This call may 144 * Sets the internal data structures that hold the fixed and wrapped buffers
72 * change the viewport. 145 * and maps.
73 * @param {!ArrayBuffer} translatedContent The new content. 146 * @param {string} textBuffer Text of the shown braille.
74 * @param {number} targetPosition Target position. The viewport is changed 147 * @param {!ArrayBuffer} translatedContent The new braille content.
75 * to overlap this position. 148 * @param {Array<number>} fixedBrailleToText Map of Braille cells to the first
149 * index of corresponding text letter.
76 */ 150 */
77 setContent: function(translatedContent, targetPosition) { 151 setContent: function(textBuffer, translatedContent, fixedBrailleToText) {
78 this.breakPoints_ = this.calculateBreakPoints_(translatedContent); 152 this.viewPort_.start = 0;
79 this.contentLength_ = translatedContent.byteLength; 153 this.viewPort_.end = this.rowCount_ - 1;
80 this.panToPosition_(targetPosition); 154 this.fixedBrailleToText_ = fixedBrailleToText;
155 this.wrappedBrailleToText_ = [];
156 this.textBuffer = textBuffer;
157 this.fixedBuffer_ = translatedContent;
158
159 // Convert the cells to Unicode braille pattern characters.
160 var view = new Uint8Array(translatedContent);
161 var wrappedBrailleArray = new Uint8Array(translatedContent.byteLength * 4);
dmazzoni 2016/11/14 21:37:37 Since Uint8Array can't be resized, it might make m
ultimatedbz 2016/11/15 18:04:02 I was thinking if I had to expand the Uint8array,
162 //TODO increase size if needed.
163
164 var lastBreak = 0;
165 var cellsPadded = 0;
166 var index;
167 for (index = 0; index < translatedContent.byteLength + cellsPadded;
168 index++) {
169 // Is index at the beginning of a new line?
170 if (index != 0 && index % this.columnCount_ == 0) {
171 if (view[index - cellsPadded] == 0) {
172 // On Delete all 0's at beginning of new line.
173 while (index - cellsPadded < view.length &&
174 view[index - cellsPadded] == 0) {
175 cellsPadded--;
176 }
177 index--;
178 } else if (view[index - cellsPadded - 1] != 0 &&
179 lastBreak % this.columnCount_ != 0) {
180 // If first cell is not empty, we need to move the whole word down to
181 // this line and padd to previous line with 0's, from |lastBreak| to
182 // index. The braille to text map is also updated.
183 // If lastBreak is at the beginning of a line, that means the current
184 // word is bigger than |this.columnCount_| so we shouldn't wrap.
185 for (var j = lastBreak + 1; j < index; j++) {
186 wrappedBrailleArray[j] = 0;
187 this.wrappedBrailleToText_[j] = this.wrappedBrailleToText_[j - 1];
188 cellsPadded++;
189 }
190 lastBreak = index;
191 index--;
192 } else {
193 wrappedBrailleArray[index] = view[index - cellsPadded];
194 this.wrappedBrailleToText_.push(
195 fixedBrailleToText[index - cellsPadded]);
196 }
197 } else {
198 if (view[index - cellsPadded] == 0) {
199 lastBreak = index;
200 }
201 wrappedBrailleArray[index] = view[index - cellsPadded];
202 this.wrappedBrailleToText_.push(
203 fixedBrailleToText[index - cellsPadded]);
204 }
205 }
206
207 // Convert the wrapped Braille Uint8 Array back to ArrayBuffer.
208 this.wrappedBuffer_ = new ArrayBuffer(wrappedBrailleArray.length);
209 new Uint8Array(this.wrappedBuffer_).set(wrappedBrailleArray);
210 this.wrappedBuffer_ = this.wrappedBuffer_.slice(0, index);
211
212 // Calculate how many lines are in the fixed and wrapped buffers.
213 this.fixedLineCount_ = this.fixedBuffer_.byteLength / this.columnCount_;
dmazzoni 2016/11/14 21:37:37 How about Math.ceil(byteLength / columnCount)?
ultimatedbz 2016/11/15 18:04:02 Oh! I forgot about Math.ceil(), thanks!
214 if (this.fixedBuffer_.byteLength % this.columnCount_ > 0)
215 this.fixedLineCount_++;
216 this.wrappedLineCount_ =
dmazzoni 2016/11/14 21:37:37 same
ultimatedbz 2016/11/15 18:04:02 Done.
217 this.wrappedBuffer_.byteLength / this.columnCount_;
218 if (this.wrappedBuffer_.byteLength % this.columnCount_ > 0)
219 this.wrappedLineCount_++;
81 }, 220 },
82 221
83 /** 222 /**
84 * If possible, changes the viewport to a part of the line that follows 223 * If possible, changes the viewport to a part of the line that follows
85 * the current viewport. 224 * the current viewport.
86 * @return {boolean} {@code true} if the viewport was changed. 225 * @return {boolean} {@code true} if the viewport was changed.
87 */ 226 */
88 next: function() { 227 next: function() {
228 var contentLength = this.panStrategyWrapped_ ?
229 this.wrappedLineCount_ : this.fixedLineCount_;
89 var newStart = this.viewPort_.end; 230 var newStart = this.viewPort_.end;
90 var newEnd; 231 var newEnd;
91 if (newStart + this.displaySize_ < this.contentLength_) { 232 if (newStart + this.rowCount_ - 1 < contentLength) {
92 newEnd = this.extendRight_(newStart); 233 newEnd = newStart + this.rowCount_ - 1;
93 } else { 234 } else {
94 newEnd = this.contentLength_; 235 newEnd = contentLength;
95 } 236 }
96 if (newEnd > newStart) { 237 if (newEnd > newStart) {
97 this.viewPort_ = {start: newStart, end: newEnd}; 238 this.viewPort_ = {start: newStart, end: newEnd};
98 return true; 239 return true;
99 } 240 }
100 return false; 241 return false;
101 }, 242 },
102 243
103 /** 244 /**
104 * If possible, changes the viewport to a part of the line that precedes 245 * If possible, changes the viewport to a part of the line that precedes
105 * the current viewport. 246 * the current viewport.
106 * @return {boolean} {@code true} if the viewport was changed. 247 * @return {boolean} {@code true} if the viewport was changed.
107 */ 248 */
108 previous: function() { 249 previous: function() {
250 var contentLength = this.panStrategyWrapped_ ?
251 this.wrappedLineCount_ : this.fixedLineCount_;
109 if (this.viewPort_.start > 0) { 252 if (this.viewPort_.start > 0) {
110 var newStart, newEnd; 253 var newStart, newEnd;
111 if (this.viewPort_.start <= this.displaySize_) { 254 if (this.viewPort_.start < this.rowCount_) {
112 newStart = 0; 255 newStart = 0;
113 newEnd = this.extendRight_(newStart); 256 newEnd = Math.min(this.rowCount_, contentLength);
114 } else { 257 } else {
115 newEnd = this.viewPort_.start; 258 newEnd = this.viewPort_.start;
116 var limit = newEnd - this.displaySize_; 259 newStart = newEnd - this.rowCount_ + 1;
117 newStart = limit;
118 var pos = 0;
119 while (pos < this.breakPoints_.length &&
120 this.breakPoints_[pos] < limit) {
121 pos++;
122 }
123 if (pos < this.breakPoints_.length &&
124 this.breakPoints_[pos] < newEnd) {
125 newStart = this.breakPoints_[pos];
126 }
127 } 260 }
128 if (newStart < newEnd) { 261 if (newStart < newEnd) {
129 this.viewPort_ = {start: newStart, end: newEnd}; 262 this.viewPort_ = {start: newStart, end: newEnd};
130 return true; 263 return true;
131 } 264 }
132 } 265 }
133 return false; 266 return false;
134 }, 267 },
135 268
136 /** 269 /**
137 * Finds the end position for a new viewport start position, considering 270 * TODO
138 * current breakpoints as well as display size and content length.
139 * @param {number} from Start of the region to extend.
140 * @return {number}
141 * @private
142 */
143 extendRight_: function(from) {
144 var limit = Math.min(from + this.displaySize_, this.contentLength_);
145 var pos = 0;
146 var result = limit;
147 while (pos < this.breakPoints_.length && this.breakPoints_[pos] <= from) {
148 pos++;
149 }
150 while (pos < this.breakPoints_.length && this.breakPoints_[pos] <= limit) {
151 result = this.breakPoints_[pos];
152 pos++;
153 }
154 return result;
155 },
156
157 /**
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
163 * than any position.
164 * @private
165 */
166 calculateBreakPoints_: function(content) {return [];},
167
168 /**
169 * Moves the viewport so that it overlaps a target position without taking 271 * Moves the viewport so that it overlaps a target position without taking
170 * the current viewport position into consideration. 272 * the current viewport position into consideration.
171 * @param {number} position Target position. 273 * @param {number} position Target position.
172 */ 274 */
173 panToPosition_: function(position) { 275 panToPosition_: function(position) {
276 /*
174 if (this.displaySize_ > 0) { 277 if (this.displaySize_ > 0) {
175 this.viewPort_ = {start: 0, end: 0}; 278 this.viewPort_ = {start: 0, end: 0};
176 while (this.next() && this.viewPort_.end <= position) { 279 while (this.next() && this.viewPort_.end <= position) {
177 // Nothing to do. 280 // Nothing to do.
178 } 281 }
179 } else { 282 } else {
180 this.viewPort_ = {start: position, end: position}; 283 this.viewPort_ = {start: position, end: position};
181 } 284 }
285 */
182 }, 286 },
183 }; 287 };
184
185 /**
186 * A pan strategy that fits as much content on the display as possible, that
187 * is, it doesn't do any wrapping.
188 * @constructor
189 * @extends {cvox.PanStrategy}
190 */
191 cvox.FixedPanStrategy = cvox.PanStrategy;
David Tseng 2016/11/14 21:05:08 Please don't remove these classes. A much better
ultimatedbz 2016/11/15 00:01:17 I was thinking that if we got multiline to work, i
192 /**
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.
196 * @constructor
197 * @extends {cvox.PanStrategy}
198 */
199 cvox.WrappingPanStrategy = function() {
200 cvox.PanStrategy.call(this);
201 };
202
203 cvox.WrappingPanStrategy.prototype = {
204 __proto__: cvox.PanStrategy.prototype,
205
206 /** @override */
207 calculateBreakPoints_: function(content) {
208 var view = new Uint8Array(content);
209 var newContentLength = view.length;
210 var result = [];
211 var lastCellWasBlank = false;
212 for (var pos = 0; pos < view.length; ++pos) {
213 if (lastCellWasBlank && view[pos] != 0) {
214 result.push(pos);
215 }
216 lastCellWasBlank = (view[pos] == 0);
217 }
218 return result;
219 },
220 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698