76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav GlassYUI.add('highlight-accentfold', function(Y) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass/**
6f5af1718dea14514e194dedfc335186cddd2a9aRyan GroveAdds accent-folding highlighters to `Y.Highlight`.
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove@module highlight
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove@submodule highlight-accentfold
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove**/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass/**
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove@class Highlight
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove@static
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove**/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glassvar AccentFold = Y.Text.AccentFold,
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Escape = Y.Escape,
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass EMPTY_OBJECT = {},
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav GlassHighlight = Y.mix(Y.Highlight, {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // -- Public Static Methods ------------------------------------------------
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass /**
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove Accent-folding version of `all()`.
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove @method allFold
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove @param {String} haystack String to apply highlighting to.
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove @param {String|String[]} needles String or array of strings that should be
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove highlighted.
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove @param {Object} [options] Options object.
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove @param {Boolean} [options.startsWith=false] If `true`, matches must be
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove anchored to the beginning of the string.
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove @return {String} Escaped and highlighted copy of _haystack_.
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove @static
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove **/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass allFold: function (haystack, needles, options) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass var template = Highlight._TEMPLATE,
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass results = [],
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass startPos = 0,
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass chunk, i, len, match, result;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass options = Y.merge({
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // This tells Highlight.all() not to escape HTML, in order to ensure
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // usable match offsets. The output of all() is discarded, and we
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // perform our own escaping before returning the highlighted string.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass escapeHTML: false,
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // While the highlight regex operates on the accent-folded strings,
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // this replacer will highlight the matched positions in the
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // original string.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass //
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // Note: this implementation doesn't handle multi-character folds,
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // like "æ" -> "ae". Doing so correctly would be prohibitively
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // expensive both in terms of code size and runtime performance, so
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // I've chosen to take the pragmatic route and just not do it at
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // all. This is one of many reasons why accent folding is best done
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // on the server.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass replacer: function (match, p1, foldedNeedle, pos) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass var len;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // Ignore matches inside HTML entities.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass if (p1 && !(/\s/).test(foldedNeedle)) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass return match;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass len = foldedNeedle.length;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass results.push([
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass haystack.substring(startPos, pos), // substring between previous match and this match
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass haystack.substr(pos, len) // match to be highlighted
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass ]);
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass startPos = pos + len;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }, options || EMPTY_OBJECT);
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // Run the highlighter on the folded strings. We don't care about the
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // output; our replacer function will build the canonical highlighted
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // string, with original accented characters.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Highlight.all(AccentFold.fold(haystack), AccentFold.fold(needles), options);
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // Tack on the remainder of the haystack that wasn't highlighted, if
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // any.
d28290607bd011b8b8d0dfadeba53475c5b98408Ryan Grove if (startPos < haystack.length) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass results.push([haystack.substr(startPos)]);
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // Highlight and escape the string.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass for (i = 0, len = results.length; i < len; ++i) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass chunk = Escape.html(results[i][0]);
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass if ((match = results[i][1])) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass chunk += template.replace(/\{s\}/g, Escape.html(match));
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass results[i] = chunk;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass return results.join('');
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass },
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass /**
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove Accent-folding version of `start()`.
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove @method startFold
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove @param {String} haystack String to apply highlighting to.
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove @param {String|String[]} needles String or array of strings that should be
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove highlighted.
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove @return {String} Escaped and highlighted copy of _haystack_.
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove @static
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove **/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass startFold: function (haystack, needles) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass return Highlight.allFold(haystack, needles, {startsWith: true});
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass },
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass /**
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove Accent-folding version of `words()`.
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove @method wordsFold
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove @param {String} haystack String to apply highlighting to.
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove @param {String|String[]} needles String or array of strings containing words
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove that should be highlighted. If a string is passed, it will be split
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove into words; if an array is passed, it is assumed to have already been
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove split.
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove @return {String} Escaped and highlighted copy of _haystack_.
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove @static
6f5af1718dea14514e194dedfc335186cddd2a9aRyan Grove **/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass wordsFold: function (haystack, needles) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass var template = Highlight._TEMPLATE;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass return Highlight.words(haystack, AccentFold.fold(needles), {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass mapper: function (word, needles) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass if (needles.hasOwnProperty(AccentFold.fold(word))) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass return template.replace(/\{s\}/g, Escape.html(word));
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass return Escape.html(word);
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass });
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass});
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass}, '@VERSION@' ,{requires:['highlight-base', 'text-accentfold']});