~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/app/javascript/lazr/yui/text/text.js

Added lazr.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
Copyright (c) 2010, Yahoo! Inc. All rights reserved.
 
3
Code licensed under the BSD License:
 
4
http://developer.yahoo.com/yui/license.html
 
5
version: 3.3.0
 
6
build: 3167
 
7
*/
 
8
YUI.add('text-accentfold', function(Y) {
 
9
 
 
10
/**
 
11
 * Text utilities.
 
12
 *
 
13
 * @module text
 
14
 * @since 3.3.0
 
15
 */
 
16
 
 
17
/**
 
18
 * Provides a basic accent folding implementation that converts common accented
 
19
 * letters (like "á") to their non-accented forms (like "a").
 
20
 *
 
21
 * @module text
 
22
 * @submodule text-accentfold
 
23
 */
 
24
 
 
25
/**
 
26
 * <p>
 
27
 * Provides a basic accent folding implementation that converts common accented
 
28
 * letters (like "á") to their non-accented forms (like "a").
 
29
 * </p>
 
30
 *
 
31
 * <p>
 
32
 * This implementation is not comprehensive, and should only be used as a last
 
33
 * resort when accent folding can't be done on the server. A comprehensive
 
34
 * accent folding implementation would require much more character data to be
 
35
 * sent to the browser, resulting in a significant performance penalty. This
 
36
 * implementation strives for a compromise between usefulness and performance.
 
37
 * </p>
 
38
 *
 
39
 * <p>
 
40
 * Accent folding is a destructive operation that can't be reversed, and may
 
41
 * change or destroy the actual meaning of the text depending on the language.
 
42
 * It should not be used on strings that will later be displayed to a user,
 
43
 * unless this is done with the understanding that linguistic meaning may be
 
44
 * lost and that you may in fact confuse or insult the user by doing so.
 
45
 * </p>
 
46
 *
 
47
 * <p>
 
48
 * When used for matching, accent folding is likely to produce erroneous matches
 
49
 * for languages in which characters with diacritics are considered different
 
50
 * from their base characters, or where correct folding would map to other
 
51
 * character sequences than just stripped characters. For example, in German
 
52
 * "ü" is a character that's clearly different from "u" and should match "ue"
 
53
 * instead. The word "betrügen" means "to defraud", while "betrugen" is the past
 
54
 * tense of "to behave". The name "Müller" is expected to match "Mueller", but
 
55
 * not "Muller". On the other hand, accent folding falls short for languages
 
56
 * where different base characters are expected to match. In Japanese, for
 
57
 * example, hiragana and katakana characters with the same pronunciation ("あ"
 
58
 * and "ア") are commonly treated as equivalent for lookups, but accent folding
 
59
 * treats them as different.
 
60
 * </p>
 
61
 *
 
62
 * @class Text.AccentFold
 
63
 * @static
 
64
 */
 
65
 
 
66
var YArray   = Y.Array,
 
67
    Text     = Y.Text,
 
68
    FoldData = Text.Data.AccentFold,
 
69
 
 
70
AccentFold = {
 
71
    // -- Public Static Methods ------------------------------------------------
 
72
 
 
73
    /**
 
74
     * Returns <code>true</code> if the specified string contains one or more
 
75
     * characters that can be folded, <code>false</code> otherwise.
 
76
     *
 
77
     * @method canFold
 
78
     * @param {String} string String to test.
 
79
     * @return {Boolean}
 
80
     * @static
 
81
     */
 
82
    canFold: function (string) {
 
83
        var letter;
 
84
 
 
85
        for (letter in FoldData) {
 
86
            if (FoldData.hasOwnProperty(letter) &&
 
87
                    string.search(FoldData[letter]) !== -1) {
 
88
                return true;
 
89
            }
 
90
        }
 
91
 
 
92
        return false;
 
93
    },
 
94
 
 
95
    /**
 
96
     * Compares the accent-folded versions of two strings and returns
 
97
     * <code>true</code> if they're the same, <code>false</code> otherwise. If
 
98
     * a custom comparison function is supplied, the accent-folded strings will
 
99
     * be passed to that function for comparison.
 
100
     *
 
101
     * @method compare
 
102
     * @param {String} a First string to compare.
 
103
     * @param {String} b Second string to compare.
 
104
     * @param {Function} func (optional) Custom comparison function. Should
 
105
     *   return a truthy or falsy value.
 
106
     * @return {Boolean} Results of the comparison.
 
107
     * @static
 
108
     */
 
109
    compare: function (a, b, func) {
 
110
        var aFolded = AccentFold.fold(a),
 
111
            bFolded = AccentFold.fold(b);
 
112
 
 
113
        return func ? !!func(aFolded, bFolded) : aFolded === bFolded;
 
114
    },
 
115
 
 
116
    /**
 
117
     * <p>
 
118
     * Returns a copy of <em>haystack</em> containing only the strings for which
 
119
     * the supplied function returns <code>true</code>.
 
120
     * </p>
 
121
     *
 
122
     * <p>
 
123
     * While comparisons will be made using accent-folded strings, the returned
 
124
     * array of matches will contain the original strings that were passed in.
 
125
     * </p>
 
126
     *
 
127
     * @method filter
 
128
     * @param {Array} haystack Array of strings to filter.
 
129
     * @param {Function} func Comparison function. Will receive an accent-folded
 
130
     *   haystack string as an argument, and should return a truthy or falsy
 
131
     *   value.
 
132
     * @return {Array} Filtered copy of <em>haystack</em>.
 
133
     * @static
 
134
     */
 
135
    filter: function (haystack, func) {
 
136
        return YArray.filter(haystack, function (item) {
 
137
            return func(AccentFold.fold(item));
 
138
        });
 
139
    },
 
140
 
 
141
    /**
 
142
     * Accent-folds the specified string or array of strings and returns a copy
 
143
     * in which common accented letters have been converted to their closest
 
144
     * non-accented, lowercase forms.
 
145
     *
 
146
     * @method fold
 
147
     * @param {String|Array} input String or array of strings to be folded.
 
148
     * @return {String|Array} Folded string or array of strings.
 
149
     * @static
 
150
     */
 
151
    fold: function (input) {
 
152
        if (Y.Lang.isArray(input)) {
 
153
            return YArray.map(input, AccentFold.fold);
 
154
        }
 
155
 
 
156
        input = input.toLowerCase();
 
157
 
 
158
        Y.Object.each(FoldData, function (regex, letter) {
 
159
            input = input.replace(regex, letter);
 
160
        });
 
161
 
 
162
        return input;
 
163
    }
 
164
};
 
165
 
 
166
Text.AccentFold = AccentFold;
 
167
 
 
168
 
 
169
}, '3.3.0' ,{requires:['array-extras', 'text-data-accentfold']});
 
170
YUI.add('text-data-accentfold', function(Y) {
 
171
 
 
172
// The following tool was very helpful in creating these mappings:
 
173
// http://unicode.org/cldr/utility/list-unicodeset.jsp?a=[:toNFKD%3D/^a/:]&abb=on
 
174
 
 
175
Y.namespace('Text.Data').AccentFold = {
 
176
    0: /[⁰₀⓪0]/gi,
 
177
    1: /[¹₁①1]/gi,
 
178
    2: /[²₂②2]/gi,
 
179
    3: /[³₃③3]/gi,
 
180
    4: /[⁴₄④4]/gi,
 
181
    5: /[⁵₅⑤5]/gi,
 
182
    6: /[⁶₆⑥6]/gi,
 
183
    7: /[⁷₇⑦7]/gi,
 
184
    8: /[⁸₈⑧8]/gi,
 
185
    9: /[⁹₉⑨9]/gi,
 
186
    a: /[ªà-åāăąǎǟǡǻȁȃȧᵃḁẚạảấầẩẫậắằẳẵặⓐa]/gi,
 
187
    b: /[ᵇḃḅḇⓑb]/gi,
 
188
    c: /[çćĉċčᶜḉⓒc]/gi,
 
189
    d: /[ďᵈḋḍḏḑḓⅾⓓd]/gi,
 
190
    e: /[è-ëēĕėęěȅȇȩᵉḕḗḙḛḝẹẻẽếềểễệₑℯⓔe]/gi,
 
191
    f: /[ᶠḟⓕf]/gi,
 
192
    g: /[ĝğġģǧǵᵍḡℊⓖg]/gi,
 
193
    h: /[ĥȟʰḣḥḧḩḫẖℎⓗh]/gi,
 
194
    i: /[ì-ïĩīĭįijǐȉȋᵢḭḯỉịⁱℹⅰⓘi]/gi,
 
195
    j: /[ĵǰʲⓙⱼj]/gi,
 
196
    k: /[ķǩᵏḱḳḵⓚk]/gi,
 
197
    l: /[ĺļľŀljˡḷḹḻḽℓⅼⓛl]/gi,
 
198
    m: /[ᵐḿṁṃⅿⓜm]/gi,
 
199
    n: /[ñńņňǹṅṇṉṋⁿⓝn]/gi,
 
200
    o: /[ºò-öōŏőơǒǫǭȍȏȫȭȯȱᵒṍṏṑṓọỏốồổỗộớờởỡợₒℴⓞo]/gi,
 
201
    p: /[ᵖṕṗⓟp]/gi,
 
202
    q: /[ʠⓠq]/gi,
 
203
    r: /[ŕŗřȑȓʳᵣṙṛṝṟⓡr]/gi,
 
204
    s: /[śŝşšſșˢṡṣṥṧṩẛⓢs]/gi,
 
205
    t: /[ţťțᵗṫṭṯṱẗⓣt]/gi,
 
206
    u: /[ù-üũūŭůűųưǔǖǘǚǜȕȗᵘᵤṳṵṷṹṻụủứừửữựⓤu]/gi,
 
207
    v: /[ᵛᵥṽṿⅴⓥv]/gi,
 
208
    w: /[ŵʷẁẃẅẇẉẘⓦw]/gi,
 
209
    x: /[ˣẋẍₓⅹⓧx]/gi,
 
210
    y: /[ýÿŷȳʸẏẙỳỵỷỹⓨy]/gi,
 
211
    z: /[źżžᶻẑẓẕⓩz]/gi
 
212
};
 
213
 
 
214
 
 
215
}, '3.3.0' );
 
216
YUI.add('text-data-wordbreak', function(Y) {
 
217
 
 
218
Y.namespace('Text.Data').WordBreak = {
 
219
    // The UnicodeSet utility is helpful for enumerating the specific code
 
220
    // points covered by each of these regular expressions:
 
221
    // http://unicode.org/cldr/utility/list-unicodeset.jsp
 
222
    //
 
223
    // The code sets from which these regexes were derived can be generated
 
224
    // by the UnicodeSet utility using the links here:
 
225
    // http://unicode.org/cldr/utility/properties.jsp?a=Word_Break#Word_Break
 
226
 
 
227
    aletter     : '[A-Za-zªµºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬˮͰ-ʹͶͷͺ-ͽΆΈ-ΊΌΎ-ΡΣ-ϵϷ-ҁҊ-ԧԱ-Ֆՙա-ևא-תװ-׳ؠ-يٮٯٱ-ۓەۥۦۮۯۺ-ۼۿܐܒ-ܯݍ-ޥޱߊ-ߪߴߵߺࠀ-ࠕࠚࠤࠨࡀ-ࡘऄ-हऽॐक़-ॡॱ-ॷॹ-ॿঅ-ঌএঐও-নপ-রলশ-হঽৎড়ঢ়য়-ৡৰৱਅ-ਊਏਐਓ-ਨਪ-ਰਲਲ਼ਵਸ਼ਸਹਖ਼-ੜਫ਼ੲ-ੴઅ-ઍએ-ઑઓ-નપ-રલળવ-હઽૐૠૡଅ-ଌଏଐଓ-ନପ-ରଲଳଵ-ହଽଡ଼ଢ଼ୟ-ୡୱஃஅ-ஊஎ-ஐஒ-கஙசஜஞடணதந-பம-ஹௐఅ-ఌఎ-ఐఒ-నప-ళవ-హఽౘౙౠౡಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹಽೞೠೡೱೲഅ-ഌഎ-ഐഒ-ഺഽൎൠൡൺ-ൿඅ-ඖක-නඳ-රලව-ෆༀཀ-ཇཉ-ཬྈ-ྌႠ-Ⴥა-ჺჼᄀ-ቈቊ-ቍቐ-ቖቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚᎀ-ᎏᎠ-Ᏼᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᛮ-ᛰᜀ-ᜌᜎ-ᜑᜠ-ᜱᝀ-ᝑᝠ-ᝬᝮ-ᝰᠠ-ᡷᢀ-ᢨᢪᢰ-ᣵᤀ-ᤜᨀ-ᨖᬅ-ᬳᭅ-ᭋᮃ-ᮠᮮᮯᯀ-ᯥᰀ-ᰣᱍ-ᱏᱚ-ᱽᳩ-ᳬᳮ-ᳱᴀ-ᶿḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙὛὝὟ-ώᾀ-ᾴᾶ-ᾼιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼⁱⁿₐ-ₜℂℇℊ-ℓℕℙ-ℝℤΩℨK-ℭℯ-ℹℼ-ℿⅅ-ⅉⅎⅠ-ↈⒶ-ⓩⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳮⴀ-ⴥⴰ-ⵥⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞⸯ々〻〼ㄅ-ㄭㄱ-ㆎㆠ-ㆺꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘟꘪꘫꙀ-ꙮꙿ-ꚗꚠ-ꛯꜗ-ꜟꜢ-ꞈꞋ-ꞎꞐꞑꞠ-ꞩꟺ-ꠁꠃ-ꠅꠇ-ꠊꠌ-ꠢꡀ-ꡳꢂ-ꢳꣲ-ꣷꣻꤊ-ꤥꤰ-ꥆꥠ-ꥼꦄ-ꦲꧏꨀ-ꨨꩀ-ꩂꩄ-ꩋꬁ-ꬆꬉ-ꬎꬑ-ꬖꬠ-ꬦꬨ-ꬮꯀ-ꯢ가-힣ힰ-ퟆퟋ-ퟻff-stﬓ-ﬗיִײַ-ﬨשׁ-זּטּ-לּמּנּסּףּפּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷻﹰ-ﹴﹶ-ﻼA-Za-zᅠ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ]', 
 
228
    midnumlet   : "['\\.‘’․﹒'.]",
 
229
    midletter   : '[:··״‧︓﹕:]',
 
230
    midnum      : '[,;;։،؍٬߸⁄︐︔﹐﹔,;]',
 
231
    numeric     : '[0-9٠-٩٫۰-۹߀-߉०-९০-৯੦-੯૦-૯୦-୯௦-௯౦-౯೦-೯൦-൯๐-๙໐-໙༠-༩၀-၉႐-႙០-៩᠐-᠙᥆-᥏᧐-᧙᪀-᪉᪐-᪙᭐-᭙᮰-᮹᱀-᱉᱐-᱙꘠-꘩꣐-꣙꤀-꤉꧐-꧙꩐-꩙꯰-꯹]',
 
232
    cr          : '\\r',
 
233
    lf          : '\\n',
 
234
    newline     : '[\u000B\u000C\u0085\u2028\u2029]',
 
235
    extend      : '[\u0300-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u0900-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B62\u0B63\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C01-\u0C03\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C82\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0D02\u0D03\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D82\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102B-\u103E\u1056-\u1059\u105E-\u1060\u1062-\u1064\u1067-\u106D\u1071-\u1074\u1082-\u108D\u108F\u109A-\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B6-\u17D3\u17DD\u180B-\u180D\u18A9\u1920-\u192B\u1930-\u193B\u19B0-\u19C0\u19C8\u19C9\u1A17-\u1A1B\u1A55-\u1A5E\u1A60-\u1A7C\u1A7F\u1B00-\u1B04\u1B34-\u1B44\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAA\u1BE6-\u1BF3\u1C24-\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF2\u1DC0-\u1DE6\u1DFC-\u1DFF\u200C\u200D\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3099\u309A\uA66F-\uA672\uA67C\uA67D\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA880\uA881\uA8B4-\uA8C4\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAA7B\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uABE3-\uABEA\uABEC\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE26\uFF9E\uFF9F]',
 
236
    format      : '[\u00AD\u0600-\u0603\u06DD\u070F\u17B4\u17B5\u200E\u200F\u202A-\u202E\u2060-\u2064\u206A-\u206F\uFEFF\uFFF9-\uFFFB]',
 
237
    katakana    : '[〱-〵゛゜゠-ヺー-ヿㇰ-ㇿ㋐-㋾㌀-㍗ヲ-ン]',
 
238
    extendnumlet: '[_‿⁀⁔︳︴﹍-﹏_]',
 
239
    punctuation : '[!-#%-*,-\\/:;?@\\[-\\]_{}¡«·»¿;·՚-՟։֊־׀׃׆׳״؉؊،؍؛؞؟٪-٭۔܀-܍߷-߹࠰-࠾࡞।॥॰෴๏๚๛༄-༒༺-༽྅࿐-࿔࿙࿚၊-၏჻፡-፨᐀᙭᙮᚛᚜᛫-᛭᜵᜶។-៖៘-៚᠀-᠊᥄᥅᨞᨟᪠-᪦᪨-᪭᭚-᭠᯼-᯿᰻-᰿᱾᱿᳓‐-‧‰-⁃⁅-⁑⁓-⁞⁽⁾₍₎〈〉❨-❵⟅⟆⟦-⟯⦃-⦘⧘-⧛⧼⧽⳹-⳼⳾⳿⵰⸀-⸮⸰⸱、-〃〈-】〔-〟〰〽゠・꓾꓿꘍-꘏꙳꙾꛲-꛷꡴-꡷꣎꣏꣸-꣺꤮꤯꥟꧁-꧍꧞꧟꩜-꩟꫞꫟꯫﴾﴿︐-︙︰-﹒﹔-﹡﹣﹨﹪﹫!-#%-*,-/:;?@[-]_{}⦅-・]'
 
240
};
 
241
 
 
242
 
 
243
}, '3.3.0' );
 
244
YUI.add('text-wordbreak', function(Y) {
 
245
 
 
246
/**
 
247
 * Provides utility methods for splitting strings on word breaks and determining
 
248
 * whether a character index represents a word boundary.
 
249
 *
 
250
 * @module text
 
251
 * @submodule text-wordbreak
 
252
 */
 
253
 
 
254
/**
 
255
 * <p>
 
256
 * Provides utility methods for splitting strings on word breaks and determining
 
257
 * whether a character index represents a word boundary, using the generic word
 
258
 * breaking algorithm defined in the Unicode Text Segmentation guidelines
 
259
 * (<a href="http://unicode.org/reports/tr29/#Word_Boundaries">Unicode Standard
 
260
 * Annex #29</a>).
 
261
 * </p>
 
262
 *
 
263
 * <p>
 
264
 * This algorithm provides a reasonable default for many languages. However, it
 
265
 * does not cover language or context specific requirements, and it does not
 
266
 * provide meaningful results at all for languages that don't use spaces between
 
267
 * words, such as Chinese, Japanese, Thai, Lao, Khmer, and others. Server-based
 
268
 * word breaking services usually provide significantly better results with
 
269
 * better performance.
 
270
 * </p>
 
271
 *
 
272
 * @class Text.WordBreak
 
273
 * @static
 
274
 */
 
275
 
 
276
var Text   = Y.Text,
 
277
    WBData = Text.Data.WordBreak,
 
278
 
 
279
// Constants representing code point classifications.
 
280
ALETTER      = 0,
 
281
MIDNUMLET    = 1,
 
282
MIDLETTER    = 2,
 
283
MIDNUM       = 3,
 
284
NUMERIC      = 4,
 
285
CR           = 5,
 
286
LF           = 6,
 
287
NEWLINE      = 7,
 
288
EXTEND       = 8,
 
289
FORMAT       = 9,
 
290
KATAKANA     = 10,
 
291
EXTENDNUMLET = 11,
 
292
OTHER        = 12,
 
293
 
 
294
// RegExp objects generated from code point data. Each regex matches a single
 
295
// character against a set of Unicode code points. The index of each item in
 
296
// this array must match its corresponding code point constant value defined
 
297
// above.
 
298
SETS = [
 
299
    new RegExp(WBData.aletter),
 
300
    new RegExp(WBData.midnumlet),
 
301
    new RegExp(WBData.midletter),
 
302
    new RegExp(WBData.midnum),
 
303
    new RegExp(WBData.numeric),
 
304
    new RegExp(WBData.cr),
 
305
    new RegExp(WBData.lf),
 
306
    new RegExp(WBData.newline),
 
307
    new RegExp(WBData.extend),
 
308
    new RegExp(WBData.format),
 
309
    new RegExp(WBData.katakana),
 
310
    new RegExp(WBData.extendnumlet)
 
311
],
 
312
 
 
313
EMPTY_STRING = '',
 
314
PUNCTUATION  = new RegExp('^' + WBData.punctuation + '$'),
 
315
WHITESPACE   = /\s/,
 
316
 
 
317
WordBreak = {
 
318
    // -- Public Static Methods ------------------------------------------------
 
319
 
 
320
    /**
 
321
     * Splits the specified string into an array of individual words.
 
322
     *
 
323
     * @method getWords
 
324
     * @param {String} string String to split.
 
325
     * @param {Object} options (optional) Options object containing zero or more
 
326
     *   of the following properties:
 
327
     *
 
328
     * <dl>
 
329
     *   <dt>ignoreCase (Boolean)</dt>
 
330
     *   <dd>
 
331
     *     If <code>true</code>, the string will be converted to lowercase
 
332
     *     before being split. Default is <code>false</code>.
 
333
     *   </dd>
 
334
     *
 
335
     *   <dt>includePunctuation (Boolean)</dt>
 
336
     *   <dd>
 
337
     *     If <code>true</code>, the returned array will include punctuation
 
338
     *     characters. Default is <code>false</code>.
 
339
     *   </dd>
 
340
     *
 
341
     *   <dt>includeWhitespace (Boolean)</dt>
 
342
     *   <dd>
 
343
     *     If <code>true</code>, the returned array will include whitespace
 
344
     *     characters. Default is <code>false</code>.
 
345
     *   </dd>
 
346
     * </dl>
 
347
     * @return {Array} Array of words.
 
348
     * @static
 
349
     */
 
350
    getWords: function (string, options) {
 
351
        var i     = 0,
 
352
            map   = WordBreak._classify(string),
 
353
            len   = map.length,
 
354
            word  = [],
 
355
            words = [],
 
356
            chr,
 
357
            includePunctuation,
 
358
            includeWhitespace;
 
359
 
 
360
        if (!options) {
 
361
            options = {};
 
362
        }
 
363
 
 
364
        if (options.ignoreCase) {
 
365
            string = string.toLowerCase();
 
366
        }
 
367
 
 
368
        includePunctuation = options.includePunctuation;
 
369
        includeWhitespace  = options.includeWhitespace;
 
370
 
 
371
        // Loop through each character in the classification map and determine
 
372
        // whether it precedes a word boundary, building an array of distinct
 
373
        // words as we go.
 
374
        for (; i < len; ++i) {
 
375
            chr = string.charAt(i);
 
376
 
 
377
            // Append this character to the current word.
 
378
            word.push(chr);
 
379
 
 
380
            // If there's a word boundary between the current character and the
 
381
            // next character, append the current word to the words array and
 
382
            // start building a new word. 
 
383
            if (WordBreak._isWordBoundary(map, i)) {
 
384
                word = word.join(EMPTY_STRING);
 
385
 
 
386
                if (word &&
 
387
                        (includeWhitespace  || !WHITESPACE.test(word)) &&
 
388
                        (includePunctuation || !PUNCTUATION.test(word))) {
 
389
                    words.push(word);
 
390
                }
 
391
 
 
392
                word = [];
 
393
            }
 
394
        }
 
395
 
 
396
        return words;
 
397
    },
 
398
 
 
399
    /**
 
400
     * Returns an array containing only unique words from the specified string.
 
401
     * For example, the string <code>'foo bar baz foo'</code> would result in
 
402
     * the array <code>['foo', 'bar', 'baz']</code>.
 
403
     *
 
404
     * @method getUniqueWords
 
405
     * @param {String} string String to split.
 
406
     * @param {Object} options (optional) Options (see <code>getWords()</code>
 
407
     *   for details).
 
408
     * @return {Array} Array of unique words.
 
409
     * @static
 
410
     */
 
411
    getUniqueWords: function (string, options) {
 
412
        return Y.Array.unique(WordBreak.getWords(string, options));
 
413
    },
 
414
 
 
415
    /**
 
416
     * <p>
 
417
     * Returns <code>true</code> if there is a word boundary between the
 
418
     * specified character index and the next character index (or the end of the
 
419
     * string).
 
420
     * </p>
 
421
     *
 
422
     * <p>
 
423
     * Note that there are always word breaks at the beginning and end of a
 
424
     * string, so <code>isWordBoundary('', 0)</code> and
 
425
     * <code>isWordBoundary('a', 0)</code> will both return <code>true</code>.
 
426
     * </p>
 
427
     *
 
428
     * @method isWordBoundary
 
429
     * @param {String} string String to test.
 
430
     * @param {Number} index Character index to test within the string.
 
431
     * @return {Boolean} <code>true</code> for a word boundary,
 
432
     *   <code>false</code> otherwise.
 
433
     * @static
 
434
     */
 
435
    isWordBoundary: function (string, index) {
 
436
        return WordBreak._isWordBoundary(WordBreak._classify(string), index);
 
437
    },
 
438
 
 
439
    // -- Protected Static Methods ---------------------------------------------
 
440
 
 
441
    /**
 
442
     * Returns a character classification map for the specified string.
 
443
     *
 
444
     * @method _classify
 
445
     * @param {String} string String to classify.
 
446
     * @return {Array} Classification map.
 
447
     * @protected
 
448
     * @static
 
449
     */
 
450
    _classify: function (string) {
 
451
        var chr,
 
452
            map          = [],
 
453
            i            = 0,
 
454
            j,
 
455
            set,
 
456
            stringLength = string.length,
 
457
            setsLength   = SETS.length,
 
458
            type;
 
459
 
 
460
        for (; i < stringLength; ++i) {
 
461
            chr  = string.charAt(i);
 
462
            type = OTHER;
 
463
 
 
464
            for (j = 0; j < setsLength; ++j) {
 
465
                set = SETS[j];
 
466
 
 
467
                if (set && set.test(chr)) {
 
468
                    type = j;
 
469
                    break;
 
470
                }
 
471
            }
 
472
 
 
473
            map.push(type);
 
474
        }
 
475
 
 
476
        return map;
 
477
    },
 
478
 
 
479
    /**
 
480
     * <p>
 
481
     * Returns <code>true</code> if there is a word boundary between the
 
482
     * specified character index and the next character index (or the end of the
 
483
     * string).
 
484
     * </p>
 
485
     *
 
486
     * <p>
 
487
     * Note that there are always word breaks at the beginning and end of a
 
488
     * string, so <code>_isWordBoundary('', 0)</code> and
 
489
     * <code>_isWordBoundary('a', 0)</code> will both return <code>true</code>.
 
490
     * </p>
 
491
     *
 
492
     * @method _isWordBoundary
 
493
     * @param {Array} map Character classification map generated by
 
494
     *   <code>_classify</code>.
 
495
     * @param {Number} index Character index to test.
 
496
     * @return {Boolean}
 
497
     * @protected
 
498
     * @static
 
499
     */
 
500
    _isWordBoundary: function (map, index) {
 
501
        var prevType,
 
502
            type     = map[index],
 
503
            nextType = map[index + 1],
 
504
            nextNextType;
 
505
 
 
506
        if (index < 0 || (index > map.length - 1 && index !== 0)) {
 
507
            return false;
 
508
        }
 
509
 
 
510
        // WB5. Don't break between most letters.
 
511
        if (type === ALETTER && nextType === ALETTER) {
 
512
            return false;
 
513
        }
 
514
 
 
515
        nextNextType = map[index + 2];
 
516
 
 
517
        // WB6. Don't break letters across certain punctuation.
 
518
        if (type === ALETTER &&
 
519
                (nextType === MIDLETTER || nextType === MIDNUMLET) &&
 
520
                nextNextType === ALETTER) {
 
521
            return false;
 
522
        }
 
523
 
 
524
        prevType = map[index - 1];
 
525
 
 
526
        // WB7. Don't break letters across certain punctuation.
 
527
        if ((type === MIDLETTER || type === MIDNUMLET) &&
 
528
                nextType === ALETTER &&
 
529
                prevType === ALETTER) {
 
530
            return false;
 
531
        }
 
532
 
 
533
        // WB8/WB9/WB10. Don't break inside sequences of digits or digits
 
534
        // adjacent to letters.
 
535
        if ((type === NUMERIC || type === ALETTER) &&
 
536
                (nextType === NUMERIC || nextType === ALETTER)) {
 
537
            return false;
 
538
        }
 
539
 
 
540
        // WB11. Don't break inside numeric sequences like "3.2" or
 
541
        // "3,456.789".
 
542
        if ((type === MIDNUM || type === MIDNUMLET) &&
 
543
                nextType === NUMERIC &&
 
544
                prevType === NUMERIC) {
 
545
            return false;
 
546
        }
 
547
 
 
548
        // WB12. Don't break inside numeric sequences like "3.2" or
 
549
        // "3,456.789".
 
550
        if (type === NUMERIC &&
 
551
                (nextType === MIDNUM || nextType === MIDNUMLET) &&
 
552
                nextNextType === NUMERIC) {
 
553
            return false;
 
554
        }
 
555
 
 
556
        // WB4. Ignore format and extend characters.
 
557
        if (type === EXTEND || type === FORMAT ||
 
558
                prevType === EXTEND || prevType === FORMAT ||
 
559
                nextType === EXTEND || nextType === FORMAT) {
 
560
            return false;
 
561
        }
 
562
 
 
563
        // WB3. Don't break inside CRLF.
 
564
        if (type === CR && nextType === LF) {
 
565
            return false;
 
566
        }
 
567
 
 
568
        // WB3a. Break before newlines (including CR and LF).
 
569
        if (type === NEWLINE || type === CR || type === LF) {
 
570
            return true;
 
571
        }
 
572
 
 
573
        // WB3b. Break after newlines (including CR and LF).
 
574
        if (nextType === NEWLINE || nextType === CR || nextType === LF) {
 
575
            return true;
 
576
        }
 
577
 
 
578
        // WB13. Don't break between Katakana characters.
 
579
        if (type === KATAKANA && nextType === KATAKANA) {
 
580
            return false;
 
581
        }
 
582
 
 
583
        // WB13a. Don't break from extenders.
 
584
        if (nextType === EXTENDNUMLET &&
 
585
                (type === ALETTER || type === NUMERIC || type === KATAKANA ||
 
586
                type === EXTENDNUMLET)) {
 
587
            return false;
 
588
        }
 
589
 
 
590
        // WB13b. Don't break from extenders.
 
591
        if (type === EXTENDNUMLET &&
 
592
                (nextType === ALETTER || nextType === NUMERIC ||
 
593
                nextType === KATAKANA)) {
 
594
            return false;
 
595
        }
 
596
 
 
597
        // Break after any character not covered by the rules above.
 
598
        return true;
 
599
    }
 
600
};
 
601
 
 
602
Text.WordBreak = WordBreak;
 
603
 
 
604
 
 
605
}, '3.3.0' ,{requires:['array-extras', 'text-data-wordbreak']});
 
606
 
 
607
 
 
608
YUI.add('text', function(Y){}, '3.3.0' ,{use:['text-accentfold', 'text-wordbreak']});
 
609