|
1 YUI.add('highlight-accentfold', function (Y, NAME) { |
|
2 |
|
3 /** |
|
4 Adds accent-folding highlighters to `Y.Highlight`. |
|
5 |
|
6 @module highlight |
|
7 @submodule highlight-accentfold |
|
8 **/ |
|
9 |
|
10 /** |
|
11 @class Highlight |
|
12 @static |
|
13 **/ |
|
14 |
|
15 var AccentFold = Y.Text.AccentFold, |
|
16 Escape = Y.Escape, |
|
17 |
|
18 EMPTY_OBJECT = {}, |
|
19 |
|
20 Highlight = Y.mix(Y.Highlight, { |
|
21 // -- Public Static Methods ------------------------------------------------ |
|
22 |
|
23 /** |
|
24 Accent-folding version of `all()`. |
|
25 |
|
26 @method allFold |
|
27 @param {String} haystack String to apply highlighting to. |
|
28 @param {String|String[]} needles String or array of strings that should be |
|
29 highlighted. |
|
30 @param {Object} [options] Options object. |
|
31 @param {Boolean} [options.startsWith=false] If `true`, matches must be |
|
32 anchored to the beginning of the string. |
|
33 @return {String} Escaped and highlighted copy of _haystack_. |
|
34 @static |
|
35 **/ |
|
36 allFold: function (haystack, needles, options) { |
|
37 var template = Highlight._TEMPLATE, |
|
38 results = [], |
|
39 startPos = 0, |
|
40 chunk, i, len, match, result; |
|
41 |
|
42 options = Y.merge({ |
|
43 // This tells Highlight.all() not to escape HTML, in order to ensure |
|
44 // usable match offsets. The output of all() is discarded, and we |
|
45 // perform our own escaping before returning the highlighted string. |
|
46 escapeHTML: false, |
|
47 |
|
48 // While the highlight regex operates on the accent-folded strings, |
|
49 // this replacer will highlight the matched positions in the |
|
50 // original string. |
|
51 // |
|
52 // Note: this implementation doesn't handle multi-character folds, |
|
53 // like "æ" -> "ae". Doing so correctly would be prohibitively |
|
54 // expensive both in terms of code size and runtime performance, so |
|
55 // I've chosen to take the pragmatic route and just not do it at |
|
56 // all. This is one of many reasons why accent folding is best done |
|
57 // on the server. |
|
58 replacer: function (match, p1, foldedNeedle, pos) { |
|
59 var len; |
|
60 |
|
61 // Ignore matches inside HTML entities. |
|
62 if (p1 && !(/\s/).test(foldedNeedle)) { |
|
63 return match; |
|
64 } |
|
65 |
|
66 len = foldedNeedle.length; |
|
67 |
|
68 results.push([ |
|
69 haystack.substring(startPos, pos), // substring between previous match and this match |
|
70 haystack.substr(pos, len) // match to be highlighted |
|
71 ]); |
|
72 |
|
73 startPos = pos + len; |
|
74 } |
|
75 }, options || EMPTY_OBJECT); |
|
76 |
|
77 // Run the highlighter on the folded strings. We don't care about the |
|
78 // output; our replacer function will build the canonical highlighted |
|
79 // string, with original accented characters. |
|
80 Highlight.all(AccentFold.fold(haystack), AccentFold.fold(needles), options); |
|
81 |
|
82 // Tack on the remainder of the haystack that wasn't highlighted, if |
|
83 // any. |
|
84 if (startPos < haystack.length) { |
|
85 results.push([haystack.substr(startPos)]); |
|
86 } |
|
87 |
|
88 // Highlight and escape the string. |
|
89 for (i = 0, len = results.length; i < len; ++i) { |
|
90 chunk = Escape.html(results[i][0]); |
|
91 |
|
92 if ((match = results[i][1])) { |
|
93 chunk += template.replace(/\{s\}/g, Escape.html(match)); |
|
94 } |
|
95 |
|
96 results[i] = chunk; |
|
97 } |
|
98 |
|
99 return results.join(''); |
|
100 }, |
|
101 |
|
102 /** |
|
103 Accent-folding version of `start()`. |
|
104 |
|
105 @method startFold |
|
106 @param {String} haystack String to apply highlighting to. |
|
107 @param {String|String[]} needles String or array of strings that should be |
|
108 highlighted. |
|
109 @return {String} Escaped and highlighted copy of _haystack_. |
|
110 @static |
|
111 **/ |
|
112 startFold: function (haystack, needles) { |
|
113 return Highlight.allFold(haystack, needles, {startsWith: true}); |
|
114 }, |
|
115 |
|
116 /** |
|
117 Accent-folding version of `words()`. |
|
118 |
|
119 @method wordsFold |
|
120 @param {String} haystack String to apply highlighting to. |
|
121 @param {String|String[]} needles String or array of strings containing words |
|
122 that should be highlighted. If a string is passed, it will be split |
|
123 into words; if an array is passed, it is assumed to have already been |
|
124 split. |
|
125 @return {String} Escaped and highlighted copy of _haystack_. |
|
126 @static |
|
127 **/ |
|
128 wordsFold: function (haystack, needles) { |
|
129 var template = Highlight._TEMPLATE; |
|
130 |
|
131 return Highlight.words(haystack, AccentFold.fold(needles), { |
|
132 mapper: function (word, needles) { |
|
133 if (needles.hasOwnProperty(AccentFold.fold(word))) { |
|
134 return template.replace(/\{s\}/g, Escape.html(word)); |
|
135 } |
|
136 |
|
137 return Escape.html(word); |
|
138 } |
|
139 }); |
|
140 } |
|
141 }); |
|
142 |
|
143 |
|
144 }, '@VERSION@', {"requires": ["highlight-base", "text-accentfold"]}); |