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

Side by Side Diff: pkg/intl/lib/bidi_utils.dart

Issue 11032035: Move Bidi functions into methods to make the docs look better (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 2 months 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 | Annotate | Revision Log
« no previous file with comments | « pkg/intl/lib/bidi_formatter.dart ('k') | pkg/intl/lib/intl.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 /** 5 /**
6 * Bidi stands for Bi-directional text. 6 * Bidi stands for Bi-directional text.
7 * According to http://en.wikipedia.org/wiki/Bi-directional_text: 7 * According to http://en.wikipedia.org/wiki/Bi-directional_text:
8 * Bi-directional text is text containing text in both text directionalities, 8 * Bi-directional text is text containing text in both text directionalities,
9 * both right-to-left (RTL) and left-to-right (LTR). It generally involves text 9 * both right-to-left (RTL) and left-to-right (LTR). It generally involves text
10 * containing different types of alphabets, but may also refer to boustrophedon, 10 * containing different types of alphabets, but may also refer to boustrophedon,
(...skipping 10 matching lines...) Expand all
21 * bidi_utils.dart directly. 21 * bidi_utils.dart directly.
22 */ 22 */
23 class TextDirection { 23 class TextDirection {
24 static const LTR = const TextDirection._('LTR', 'ltr'); 24 static const LTR = const TextDirection._('LTR', 'ltr');
25 static const RTL = const TextDirection._('RTL', 'rtl'); 25 static const RTL = const TextDirection._('RTL', 'rtl');
26 // If the directionality of the text cannot be determined and we are not using 26 // If the directionality of the text cannot be determined and we are not using
27 // the context direction (or if the context direction is unknown), then the 27 // the context direction (or if the context direction is unknown), then the
28 // text falls back on the more common ltr direction. 28 // text falls back on the more common ltr direction.
29 static const UNKNOWN = const TextDirection._('UNKNOWN', 'ltr'); 29 static const UNKNOWN = const TextDirection._('UNKNOWN', 'ltr');
30 30
31 /** 31 /**
32 * Textual representation of the directionality constant. One of 32 * Textual representation of the directionality constant. One of
33 * 'LTR', 'RTL', or 'UNKNOWN'. 33 * 'LTR', 'RTL', or 'UNKNOWN'.
34 */ 34 */
35 final String value; 35 final String value;
36 36
37 /** Textual representation of the directionality when used in span tag. */ 37 /** Textual representation of the directionality when used in span tag. */
38 final String spanText; 38 final String spanText;
39 39
40 const TextDirection._(this.value, this.spanText); 40 const TextDirection._(this.value, this.spanText);
41 41
42 /** 42 /**
43 * Returns true if [otherDirection] is known to be different from this 43 * Returns true if [otherDirection] is known to be different from this
44 * direction. 44 * direction.
45 */ 45 */
46 bool isDirectionChange(TextDirection otherDirection) { 46 bool isDirectionChange(TextDirection otherDirection) {
47 return otherDirection != TextDirection.UNKNOWN && this != otherDirection; 47 return otherDirection != TextDirection.UNKNOWN && this != otherDirection;
48 } 48 }
49 } 49 }
50 50
51 /**
52 * This provides a number of utility methods for working with bidirectional
53 * text. #######
Emily Fortuna 2012/10/04 18:37:55 is this a marker for more documentation suggested?
Alan Knight 2012/10/04 19:34:22 Oops. That was a temporary marker that I should ha
54 */
55 class Bidi {
56
51 /** Unicode "Left-To-Right Embedding" (LRE) character. */ 57 /** Unicode "Left-To-Right Embedding" (LRE) character. */
Emily Fortuna 2012/10/04 18:37:55 can you fix the indentation of all of these lines
Alan Knight 2012/10/04 19:34:22 Done.
52 const LRE = '\u202A'; 58 static const LRE = '\u202A';
53 59
54 /** Unicode "Right-To-Left Embedding" (RLE) character. */ 60 /** Unicode "Right-To-Left Embedding" (RLE) character. */
55 const RLE = '\u202B'; 61 static const RLE = '\u202B';
56 62
57 /** Unicode "Pop Directional Formatting" (PDF) character. */ 63 /** Unicode "Pop Directional Formatting" (PDF) character. */
58 const PDF = '\u202C'; 64 static const PDF = '\u202C';
59 65
60 /** Unicode "Left-To-Right Mark" (LRM) character. */ 66 /** Unicode "Left-To-Right Mark" (LRM) character. */
61 const LRM = '\u200E'; 67 static const LRM = '\u200E';
62 68
63 /** Unicode "Right-To-Left Mark" (RLM) character. */ 69 /** Unicode "Right-To-Left Mark" (RLM) character. */
64 const RLM = '\u200F'; 70 static const RLM = '\u200F';
65 71
66 /** Constant to define the threshold of RTL directionality. */ 72 /** Constant to define the threshold of RTL directionality. */
67 num _RTL_DETECTION_THRESHOLD = 0.40; 73 static num _RTL_DETECTION_THRESHOLD = 0.40;
68 74
69 /** 75 /**
70 * Practical patterns to identify strong LTR and RTL characters, respectively. 76 * Practical patterns to identify strong LTR and RTL characters, respectively.
71 * These patterns are not completely correct according to the Unicode 77 * These patterns are not completely correct according to the Unicode
72 * standard. They are simplified for performance and small code size. 78 * standard. They are simplified for performance and small code size.
73 */ 79 */
74 const String _LTR_CHARS = 80 static const String _LTR_CHARS =
75 r'A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02B8\u0300-\u0590' 81 r'A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02B8\u0300-\u0590'
76 r'\u0800-\u1FFF\u2C00-\uFB1C\uFDFE-\uFE6F\uFEFD-\uFFFF'; 82 r'\u0800-\u1FFF\u2C00-\uFB1C\uFDFE-\uFE6F\uFEFD-\uFFFF';
77 const String _RTL_CHARS = r'\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC'; 83 static const String _RTL_CHARS = r'\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC';
78 84
79 /** 85 /**
80 * Returns the input [text] with spaces instead of HTML tags or HTML escapes, 86 * Returns the input [text] with spaces instead of HTML tags or HTML escapes,
81 * which is helpful for text directionality estimation. 87 * which is helpful for text directionality estimation.
82 * Note: This function should not be used in other contexts. 88 * Note: This function should not be used in other contexts.
83 * It does not deal well with many things: comments, script, 89 * It does not deal well with many things: comments, script,
84 * elements, style elements, dir attribute,`>` in quoted attribute values, 90 * elements, style elements, dir attribute,`>` in quoted attribute values,
85 * etc. But it does handle well enough the most common use cases. 91 * etc. But it does handle well enough the most common use cases.
86 * Since the worst that can happen as a result of these shortcomings is that 92 * Since the worst that can happen as a result of these shortcomings is that
87 * the wrong directionality will be estimated, we have not invested in 93 * the wrong directionality will be estimated, we have not invested in
88 * improving this. 94 * improving this.
89 */ 95 */
90 String stripHtmlIfNeeded(String text) { 96 static String stripHtmlIfNeeded(String text) {
91 // The regular expression is simplified for an HTML tag (opening or 97 // The regular expression is simplified for an HTML tag (opening or
92 // closing) or an HTML escape. We might want to skip over such expressions 98 // closing) or an HTML escape. We might want to skip over such expressions
93 // when estimating the text directionality. 99 // when estimating the text directionality.
94 return text.replaceAll(const RegExp(r'<[^>]*>|&[^;]+;'), ' '); 100 return text.replaceAll(const RegExp(r'<[^>]*>|&[^;]+;'), ' ');
95 } 101 }
96 102
97 /** 103 /**
98 * Determines if the first character in [text] with strong directionality is 104 * Determines if the first character in [text] with strong directionality is
99 * LTR. If [isHtml] is true, the text is HTML or HTML-escaped. 105 * LTR. If [isHtml] is true, the text is HTML or HTML-escaped.
100 */ 106 */
101 bool startsWithLtr(String text, [isHtml=false]) { 107 static bool startsWithLtr(String text, [isHtml=false]) {
102 return const RegExp('^[^$_RTL_CHARS]*[$_LTR_CHARS]').hasMatch( 108 return const RegExp('^[^$_RTL_CHARS]*[$_LTR_CHARS]').hasMatch(
103 isHtml? stripHtmlIfNeeded(text) : text); 109 isHtml? stripHtmlIfNeeded(text) : text);
104 } 110 }
105 111
106 /** 112 /**
107 * Determines if the first character in [text] with strong directionality is 113 * Determines if the first character in [text] with strong directionality is
108 * RTL. If [isHtml] is true, the text is HTML or HTML-escaped. 114 * RTL. If [isHtml] is true, the text is HTML or HTML-escaped.
109 */ 115 */
110 bool startsWithRtl(String text, [isHtml=false]) { 116 static bool startsWithRtl(String text, [isHtml=false]) {
111 return const RegExp('^[^$_LTR_CHARS]*[$_RTL_CHARS]').hasMatch( 117 return const RegExp('^[^$_LTR_CHARS]*[$_RTL_CHARS]').hasMatch(
112 isHtml? stripHtmlIfNeeded(text) : text); 118 isHtml? stripHtmlIfNeeded(text) : text);
113 } 119 }
114 120
115 /** 121 /**
116 * Determines if the exit directionality (ie, the last strongly-directional 122 * Determines if the exit directionality (ie, the last strongly-directional
117 * character in [text] is LTR. If [isHtml] is true, the text is HTML or 123 * character in [text] is LTR. If [isHtml] is true, the text is HTML or
118 * HTML-escaped. 124 * HTML-escaped.
119 */ 125 */
120 bool endsWithLtr(String text, [isHtml=false]) { 126 static bool endsWithLtr(String text, [isHtml=false]) {
121 return const RegExp('[$_LTR_CHARS][^$_RTL_CHARS]*\$').hasMatch( 127 return const RegExp('[$_LTR_CHARS][^$_RTL_CHARS]*\$').hasMatch(
122 isHtml? stripHtmlIfNeeded(text) : text); 128 isHtml? stripHtmlIfNeeded(text) : text);
123 } 129 }
124 130
125 /** 131 /**
126 * Determines if the exit directionality (ie, the last strongly-directional 132 * Determines if the exit directionality (ie, the last strongly-directional
127 * character in [text] is RTL. If [isHtml] is true, the text is HTML or 133 * character in [text] is RTL. If [isHtml] is true, the text is HTML or
128 * HTML-escaped. 134 * HTML-escaped.
129 */ 135 */
130 bool endsWithRtl(String text, [isHtml=false]) { 136 static bool endsWithRtl(String text, [isHtml=false]) {
131 return const RegExp('[$_RTL_CHARS][^$_LTR_CHARS]*\$').hasMatch( 137 return const RegExp('[$_RTL_CHARS][^$_LTR_CHARS]*\$').hasMatch(
132 isHtml? stripHtmlIfNeeded(text) : text); 138 isHtml? stripHtmlIfNeeded(text) : text);
133 } 139 }
134 140
135 /** 141 /**
136 * Determines if the given [text] has any LTR characters in it. 142 * Determines if the given [text] has any LTR characters in it.
137 * If [isHtml] is true, the text is HTML or HTML-escaped. 143 * If [isHtml] is true, the text is HTML or HTML-escaped.
138 */ 144 */
139 bool hasAnyLtr(String text, [isHtml=false]) { 145 static bool hasAnyLtr(String text, [isHtml=false]) {
140 return const RegExp(r'[' '$_LTR_CHARS' r']').hasMatch( 146 return const RegExp(r'[' '$_LTR_CHARS' r']').hasMatch(
141 isHtml? stripHtmlIfNeeded(text) : text); 147 isHtml? stripHtmlIfNeeded(text) : text);
142 } 148 }
143 149
144 /** 150 /**
145 * Determines if the given [text] has any RTL characters in it. 151 * Determines if the given [text] has any RTL characters in it.
146 * If [isHtml] is true, the text is HTML or HTML-escaped. 152 * If [isHtml] is true, the text is HTML or HTML-escaped.
147 */ 153 */
148 bool hasAnyRtl(String text, [isHtml=false]) { 154 static bool hasAnyRtl(String text, [isHtml=false]) {
149 return const RegExp(r'[' '$_RTL_CHARS' r']').hasMatch( 155 return const RegExp(r'[' '$_RTL_CHARS' r']').hasMatch(
150 isHtml? stripHtmlIfNeeded(text) : text); 156 isHtml? stripHtmlIfNeeded(text) : text);
151 } 157 }
152 158
153 /** 159 /**
154 * Check if a BCP 47 / III [languageString] indicates an RTL language. 160 * Check if a BCP 47 / III [languageString] indicates an RTL language.
155 * 161 *
156 * i.e. either: 162 * i.e. either:
157 * - a language code explicitly specifying one of the right-to-left scripts, 163 * - a language code explicitly specifying one of the right-to-left scripts,
158 * e.g. "az-Arab", or 164 * e.g. "az-Arab", or
159 * - a language code specifying one of the languages normally written in a 165 * - a language code specifying one of the languages normally written in a
160 * right-to-left script, e.g. "fa" (Farsi), except ones explicitly 166 * right-to-left script, e.g. "fa" (Farsi), except ones explicitly
161 * specifying Latin or Cyrillic script (which are the usual LTR 167 * specifying Latin or Cyrillic script (which are the usual LTR
162 * alternatives). 168 * alternatives).
163 * 169 *
164 * The list of right-to-left scripts appears in the 100-199 range in 170 * The list of right-to-left scripts appears in the 100-199 range in
165 * http://www.unicode.org/iso15924/iso15924-num.html, of which Arabic and 171 * http://www.unicode.org/iso15924/iso15924-num.html, of which Arabic and
166 * Hebrew are by far the most widely used. We also recognize Thaana, N'Ko, and 172 * Hebrew are by far the most widely used. We also recognize Thaana, N'Ko, and
167 * Tifinagh, which also have significant modern usage. The rest (Syriac, 173 * Tifinagh, which also have significant modern usage. The rest (Syriac,
168 * Samaritan, Mandaic, etc.) seem to have extremely limited or no modern usage 174 * Samaritan, Mandaic, etc.) seem to have extremely limited or no modern usage
169 * and are not recognized. 175 * and are not recognized.
170 * The languages usually written in a right-to-left script are taken as those 176 * The languages usually written in a right-to-left script are taken as those
171 * with Suppress-Script: Hebr|Arab|Thaa|Nkoo|Tfng in 177 * with Suppress-Script: Hebr|Arab|Thaa|Nkoo|Tfng in
172 * http://www.iana.org/assignments/language-subtag-registry, 178 * http://www.iana.org/assignments/language-subtag-registry,
173 * as well as Sindhi (sd) and Uyghur (ug). 179 * as well as Sindhi (sd) and Uyghur (ug).
174 * The presence of other subtags of the language code, e.g. regions like EG 180 * The presence of other subtags of the language code, e.g. regions like EG
175 * (Egypt), is ignored. 181 * (Egypt), is ignored.
176 */ 182 */
177 bool isRtlLanguage(String languageString) { 183 static bool isRtlLanguage(String languageString) {
178 return const RegExp(r'^(ar|dv|he|iw|fa|nqo|ps|sd|ug|ur|yi|.*[-_]' 184 return const RegExp(r'^(ar|dv|he|iw|fa|nqo|ps|sd|ug|ur|yi|.*[-_]'
179 r'(Arab|Hebr|Thaa|Nkoo|Tfng))(?!.*[-_](Latn|Cyrl)($|-|_))' 185 r'(Arab|Hebr|Thaa|Nkoo|Tfng))(?!.*[-_](Latn|Cyrl)($|-|_))'
180 r'($|-|_)', ignoreCase : true).hasMatch(languageString); 186 r'($|-|_)', ignoreCase : true).hasMatch(languageString);
181 } 187 }
182 188
183 /** 189 /**
184 * Enforce the [html] snippet in RTL directionality regardless of overall 190 * Enforce the [html] snippet in RTL directionality regardless of overall
185 * context. If the html piece was enclosed by a tag, the direction will be 191 * context. If the html piece was enclosed by a tag, the direction will be
186 * applied to existing tag, otherwise a span tag will be added as wrapper. 192 * applied to existing tag, otherwise a span tag will be added as wrapper.
187 * For this reason, if html snippet start with with tag, this tag must enclose 193 * For this reason, if html snippet start with with tag, this tag must enclose
188 * the whole piece. If the tag already has a direction specified, this new one 194 * the whole piece. If the tag already has a direction specified, this new one
189 * will override existing one in behavior (should work on Chrome, FF, and IE 195 * will override existing one in behavior (should work on Chrome, FF, and IE
190 * since this was ported directly from the Closure version). 196 * since this was ported directly from the Closure version).
191 */ 197 */
192 String enforceRtlInHtml(String html) { 198 static String enforceRtlInHtml(String html) {
193 return _enforceInHtmlHelper(html, 'rtl'); 199 return _enforceInHtmlHelper(html, 'rtl');
194 } 200 }
195 201
196 /** 202 /**
197 * Enforce RTL on both end of the given [text] using unicode BiDi formatting 203 * Enforce RTL on both end of the given [text] using unicode BiDi formatting
198 * characters RLE and PDF. 204 * characters RLE and PDF.
199 */ 205 */
200 String enforceRtlInText(String text) { 206 static String enforceRtlInText(String text) {
201 return '$RLE$text$PDF'; 207 return '$RLE$text$PDF';
202 } 208 }
203 209
204 /** 210 /**
205 * Enforce the [html] snippet in LTR directionality regardless of overall 211 * Enforce the [html] snippet in LTR directionality regardless of overall
206 * context. If the html piece was enclosed by a tag, the direction will be 212 * context. If the html piece was enclosed by a tag, the direction will be
207 * applied to existing tag, otherwise a span tag will be added as wrapper. 213 * applied to existing tag, otherwise a span tag will be added as wrapper.
208 * For this reason, if html snippet start with with tag, this tag must enclose 214 * For this reason, if html snippet start with with tag, this tag must enclose
209 * the whole piece. If the tag already has a direction specified, this new one 215 * the whole piece. If the tag already has a direction specified, this new one
210 * will override existing one in behavior (tested on FF and IE). 216 * will override existing one in behavior (tested on FF and IE).
211 */ 217 */
212 String enforceLtrInHtml(String html) { 218 static String enforceLtrInHtml(String html) {
213 return _enforceInHtmlHelper(html, 'ltr'); 219 return _enforceInHtmlHelper(html, 'ltr');
214 } 220 }
215 221
216 /** 222 /**
217 * Enforce LTR on both end of the given [text] using unicode BiDi formatting 223 * Enforce LTR on both end of the given [text] using unicode BiDi formatting
218 * characters LRE and PDF. 224 * characters LRE and PDF.
219 */ 225 */
220 String enforceLtrInText(String text) { 226 static String enforceLtrInText(String text) {
221 return '$LRE$text$PDF'; 227 return '$LRE$text$PDF';
222 } 228 }
223 229
224 /** 230 /**
225 * Enforce the [html] snippet in the desired [direction] regardless of overall 231 * Enforce the [html] snippet in the desired [direction] regardless of overall
226 * context. If the html piece was enclosed by a tag, the direction will be 232 * context. If the html piece was enclosed by a tag, the direction will be
227 * applied to existing tag, otherwise a span tag will be added as wrapper. 233 * applied to existing tag, otherwise a span tag will be added as wrapper.
228 * For this reason, if html snippet start with with tag, this tag must enclose 234 * For this reason, if html snippet start with with tag, this tag must enclose
229 * the whole piece. If the tag already has a direction specified, this new one 235 * the whole piece. If the tag already has a direction specified, this new one
230 * will override existing one in behavior (tested on FF and IE). 236 * will override existing one in behavior (tested on FF and IE).
231 */ 237 */
232 String _enforceInHtmlHelper(String html, String direction) { 238 static String _enforceInHtmlHelper(String html, String direction) {
233 if (html.startsWith('<')) { 239 if (html.startsWith('<')) {
234 StringBuffer buffer = new StringBuffer(); 240 StringBuffer buffer = new StringBuffer();
235 var startIndex = 0; 241 var startIndex = 0;
236 Match match = const RegExp('<\\w+').firstMatch(html); 242 Match match = const RegExp('<\\w+').firstMatch(html);
237 if (match != null) { 243 if (match != null) {
238 buffer.add(html.substring( 244 buffer.add(html.substring(
239 startIndex, match.end())).add(' dir=$direction'); 245 startIndex, match.end())).add(' dir=$direction');
240 startIndex = match.end(); 246 startIndex = match.end();
241 } 247 }
242 return buffer.add(html.substring(startIndex)).toString(); 248 return buffer.add(html.substring(startIndex)).toString();
243 } 249 }
244 // '\n' is important for FF so that it won't incorrectly merge span groups. 250 // '\n' is important for FF so that it won't incorrectly merge span groups.
245 return '\n<span dir=$direction>$html</span>'; 251 return '\n<span dir=$direction>$html</span>';
246 } 252 }
247 253
248 /** 254 /**
249 * Apply bracket guard to [str] using html span tag. This is to address the 255 * Apply bracket guard to [str] using html span tag. This is to address the
250 * problem of messy bracket display that frequently happens in RTL layout. 256 * problem of messy bracket display that frequently happens in RTL layout.
251 * If [isRtlContext] is true, then we explicitly want to wrap in a span of RTL 257 * If [isRtlContext] is true, then we explicitly want to wrap in a span of RTL
252 * directionality, regardless of the estimated directionality. 258 * directionality, regardless of the estimated directionality.
253 */ 259 */
254 String guardBracketInHtml(String str, [bool isRtlContext]) { 260 static String guardBracketInHtml(String str, [bool isRtlContext]) {
255 var useRtl = isRtlContext == null ? hasAnyRtl(str) : isRtlContext; 261 var useRtl = isRtlContext == null ? hasAnyRtl(str) : isRtlContext;
256 RegExp matchingBrackets = 262 RegExp matchingBrackets =
257 const RegExp(r'(\(.*?\)+)|(\[.*?\]+)|(\{.*?\}+)|(&lt;.*?(&gt;)+)'); 263 const RegExp(r'(\(.*?\)+)|(\[.*?\]+)|(\{.*?\}+)|(&lt;.*?(&gt;)+)');
258 return _guardBracketHelper(str, matchingBrackets, 264 return _guardBracketHelper(str, matchingBrackets,
259 '<span dir=${useRtl? "rtl" : "ltr"}>', '</span>'); 265 '<span dir=${useRtl? "rtl" : "ltr"}>', '</span>');
260 } 266 }
261 267
262 /** 268 /**
263 * Apply bracket guard to [str] using LRM and RLM. This is to address the 269 * Apply bracket guard to [str] using LRM and RLM. This is to address the
264 * problem of messy bracket display that frequently happens in RTL layout. 270 * problem of messy bracket display that frequently happens in RTL layout.
265 * This version works for both plain text and html, but in some cases is not 271 * This version works for both plain text and html, but in some cases is not
266 * as good as guardBracketInHtml. 272 * as good as guardBracketInHtml.
267 * If [isRtlContext] is true, then we explicitly want to wrap in a span of RTL 273 * If [isRtlContext] is true, then we explicitly want to wrap in a span of RTL
268 * directionality, regardless of the estimated directionality. 274 * directionality, regardless of the estimated directionality.
269 */ 275 */
270 String guardBracketInText(String str, [bool isRtlContext]) { 276 static String guardBracketInText(String str, [bool isRtlContext]) {
271 var useRtl = isRtlContext == null ? hasAnyRtl(str) : isRtlContext; 277 var useRtl = isRtlContext == null ? hasAnyRtl(str) : isRtlContext;
272 var mark = useRtl ? RLM : LRM; 278 var mark = useRtl ? RLM : LRM;
273 return _guardBracketHelper(str, 279 return _guardBracketHelper(str,
274 const RegExp(r'(\(.*?\)+)|(\[.*?\]+)|(\{.*?\}+)|(<.*?>+)'), mark, mark); 280 const RegExp(r'(\(.*?\)+)|(\[.*?\]+)|(\{.*?\}+)|(<.*?>+)'), mark, mark);
275 } 281 }
276 282
277 /** 283 /**
278 * (Mostly) reimplements the $& functionality of "replace" in JavaScript. 284 * (Mostly) reimplements the $& functionality of "replace" in JavaScript.
279 * Given a [str] and the [regexp] to match with, optionally supply a string to 285 * Given a [str] and the [regexp] to match with, optionally supply a string to
280 * be inserted [before] the match and/or [after]. For example, 286 * be inserted [before] the match and/or [after]. For example,
281 * `_guardBracketHelper('firetruck', const RegExp('truck'), 'hydrant', '!')` 287 * `_guardBracketHelper('firetruck', const RegExp('truck'), 'hydrant', '!')`
282 * would return 'firehydrant!'. 288 * would return 'firehydrant!'.
283 */ 289 */
284 // TODO(efortuna): Get rid of this once this is implemented in Dart. 290 // TODO(efortuna): Get rid of this once this is implemented in Dart.
285 // See Issue 2979. 291 // See Issue 2979.
286 String _guardBracketHelper(String str, RegExp regexp, [String before, 292 static String _guardBracketHelper(String str, RegExp regexp, [String before,
287 String after]) { 293 String after]) {
288 StringBuffer buffer = new StringBuffer(); 294 StringBuffer buffer = new StringBuffer();
289 var startIndex = 0; 295 var startIndex = 0;
290 Iterable matches = regexp.allMatches(str); 296 Iterable matches = regexp.allMatches(str);
291 for (Match match in matches) { 297 for (Match match in matches) {
292 buffer.add(str.substring(startIndex, match.start())).add(before); 298 buffer.add(str.substring(startIndex, match.start())).add(before);
293 buffer.add(str.substring(match.start(), match.end())).add(after); 299 buffer.add(str.substring(match.start(), match.end())).add(after);
294 startIndex = match.end(); 300 startIndex = match.end();
295 } 301 }
296 return buffer.add(str.substring(startIndex)).toString(); 302 return buffer.add(str.substring(startIndex)).toString();
297 } 303 }
298 304
299 /** 305 /**
300 * Estimates the directionality of [text] using the best known 306 * Estimates the directionality of [text] using the best known
301 * general-purpose method (using relative word counts). A 307 * general-purpose method (using relative word counts). A
302 * TextDirection.UNKNOWN return value indicates completely neutral input. 308 * TextDirection.UNKNOWN return value indicates completely neutral input.
303 * [isHtml] is true if [text] HTML or HTML-escaped. 309 * [isHtml] is true if [text] HTML or HTML-escaped.
304 * 310 *
305 * If the number of RTL words is above a certain percentage of the total 311 * If the number of RTL words is above a certain percentage of the total
306 * number of strongly directional words, returns RTL. 312 * number of strongly directional words, returns RTL.
307 * Otherwise, if any words are strongly or weakly LTR, returns LTR. 313 * Otherwise, if any words are strongly or weakly LTR, returns LTR.
308 * Otherwise, returns UNKNOWN, which is used to mean `neutral`. 314 * Otherwise, returns UNKNOWN, which is used to mean `neutral`.
309 * Numbers and URLs are counted as weakly LTR. 315 * Numbers and URLs are counted as weakly LTR.
310 */ 316 */
311 TextDirection estimateDirectionOfText(String text, [bool isHtml=false]) { 317 static TextDirection estimateDirectionOfText(String text, [bool isHtml=false]) {
312 text = isHtml? stripHtmlIfNeeded(text) : text; 318 text = isHtml? stripHtmlIfNeeded(text) : text;
313 var rtlCount = 0; 319 var rtlCount = 0;
314 var total = 0; 320 var total = 0;
315 var hasWeaklyLtr = false; 321 var hasWeaklyLtr = false;
316 // Split a string into 'words' for directionality estimation based on 322 // Split a string into 'words' for directionality estimation based on
317 // relative word counts. 323 // relative word counts.
318 for (String token in text.split(const RegExp(r'\s+'))) { 324 for (String token in text.split(const RegExp(r'\s+'))) {
319 if (startsWithRtl(token)) { 325 if (startsWithRtl(token)) {
320 rtlCount++; 326 rtlCount++;
321 total++; 327 total++;
(...skipping 15 matching lines...) Expand all
337 return TextDirection.RTL; 343 return TextDirection.RTL;
338 } else { 344 } else {
339 return TextDirection.LTR; 345 return TextDirection.LTR;
340 } 346 }
341 } 347 }
342 348
343 /** 349 /**
344 * Find the first index in [str] of the first closing parenthesis that does 350 * Find the first index in [str] of the first closing parenthesis that does
345 * not match an opening parenthesis. 351 * not match an opening parenthesis.
346 */ 352 */
347 int _unmatchedParenIndex(String str) { 353 static int _unmatchedParenIndex(String str) {
348 int sum = 0; 354 int sum = 0;
349 int index = 0; 355 int index = 0;
350 while (sum >= 0 || index > str.length) { 356 while (sum >= 0 || index > str.length) {
351 int char = str.charCodeAt(index); 357 int char = str.charCodeAt(index);
352 if (char == '('.charCodeAt(0)) sum++; 358 if (char == '('.charCodeAt(0)) sum++;
353 else if (char == ')'.charCodeAt(0)) sum--; 359 else if (char == ')'.charCodeAt(0)) sum--;
354 index++; 360 index++;
355 } 361 }
356 return index; 362 return index;
357 } 363 }
358 364
359 /** 365 /**
360 * Replace the double and single quote directly after a Hebrew character in 366 * Replace the double and single quote directly after a Hebrew character in
361 * [str] with GERESH and GERSHAYIM. This is most likely the user's intention. 367 * [str] with GERESH and GERSHAYIM. This is most likely the user's intention.
362 */ 368 */
363 String normalizeHebrewQuote(String str) { 369 static String normalizeHebrewQuote(String str) {
364 StringBuffer buf = new StringBuffer(); 370 StringBuffer buf = new StringBuffer();
365 if (str.length > 0) { 371 if (str.length > 0) {
366 buf.add(str.substring(0, 1)); 372 buf.add(str.substring(0, 1));
367 } 373 }
368 // Start at 1 because we're looking for the patterns [\u0591-\u05f2])" or 374 // Start at 1 because we're looking for the patterns [\u0591-\u05f2])" or
369 // [\u0591-\u05f2]'. 375 // [\u0591-\u05f2]'.
370 for (int i = 1; i < str.length; i++) { 376 for (int i = 1; i < str.length; i++) {
371 if (str.substring(i, i+1) == '"' 377 if (str.substring(i, i+1) == '"'
372 && const RegExp('[\u0591-\u05f2]').hasMatch(str.substring(i-1, i))) { 378 && const RegExp('[\u0591-\u05f2]').hasMatch(str.substring(i-1, i))) {
373 buf.add('\u05f4'); 379 buf.add('\u05f4');
374 } else if (str.substring(i, i+1) == "'" 380 } else if (str.substring(i, i+1) == "'"
375 && const RegExp('[\u0591-\u05f2]').hasMatch(str.substring(i-1, i))) { 381 && const RegExp('[\u0591-\u05f2]').hasMatch(str.substring(i-1, i))) {
376 buf.add('\u05f3'); 382 buf.add('\u05f3');
377 } else { 383 } else {
378 buf.add(str.substring(i, i+1)); 384 buf.add(str.substring(i, i+1));
379 } 385 }
380 } 386 }
381 return buf.toString(); 387 return buf.toString();
382 } 388 }
383 389
384 /** 390 /**
385 * Check the estimated directionality of [str], return true if the piece of 391 * Check the estimated directionality of [str], return true if the piece of
386 * text should be laid out in RTL direction. If [isHtml] is true, the string 392 * text should be laid out in RTL direction. If [isHtml] is true, the string
387 * is HTML or HTML-escaped. 393 * is HTML or HTML-escaped.
388 */ 394 */
389 bool detectRtlDirectionality(String str, [bool isHtml]) { 395 static bool detectRtlDirectionality(String str, [bool isHtml]) {
390 return estimateDirectionOfText(str, isHtml) == TextDirection.RTL; 396 return estimateDirectionOfText(str, isHtml) == TextDirection.RTL;
391 } 397 }
398
Emily Fortuna 2012/10/04 18:37:55 You're going to love this... delete this extra lin
Alan Knight 2012/10/04 19:34:22 Done.
399 }
OLDNEW
« no previous file with comments | « pkg/intl/lib/bidi_formatter.dart ('k') | pkg/intl/lib/intl.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698