Merge "Improve sorting on SpecialWanted*-Pages"
[mediawiki.git] / resources / src / jquery / jquery.highlightText.js
bloba14e3ebd3820c09c34d28784c281c3bce8a5f8a0
1 /**
2  * Plugin that highlights matched word partials in a given element.
3  * TODO: Add a function for restoring the previous text.
4  * TODO: Accept mappings for converting shortcuts like WP: to Wikipedia:.
5  */
6 ( function ( $, mw ) {
8         $.highlightText = {
10                 // Split our pattern string at spaces and run our highlight function on the results
11                 splitAndHighlight: function ( node, text ) {
12                         var i,
13                                 words = text.split( ' ' );
14                         for ( i = 0; i < words.length; i++ ) {
15                                 if ( words[ i ].length === 0 ) {
16                                         continue;
17                                 }
18                                 $.highlightText.innerHighlight(
19                                         node,
20                                         new RegExp( '(^|\\s)' + mw.RegExp.escape( words[ i ] ), 'i' )
21                                 );
22                         }
23                         return node;
24                 },
26                 prefixHighlight: function ( node, prefix ) {
27                         $.highlightText.innerHighlight(
28                                 node,
29                                 new RegExp( '(^)' + mw.RegExp.escape( prefix ), 'i' )
30                         );
31                 },
33                 // scans a node looking for the pattern and wraps a span around each match
34                 innerHighlight: function ( node, pat ) {
35                         var i, match, pos, spannode, middlebit, middleclone;
36                         if ( node.nodeType === Node.TEXT_NODE ) {
37                                 // TODO - need to be smarter about the character matching here.
38                                 // non latin characters can make regex think a new word has begun: do not use \b
39                                 // http://stackoverflow.com/questions/3787072/regex-wordwrap-with-utf8-characters-in-js
40                                 // look for an occurrence of our pattern and store the starting position
41                                 match = node.data.match( pat );
42                                 if ( match ) {
43                                         pos = match.index + match[ 1 ].length; // include length of any matched spaces
44                                         // create the span wrapper for the matched text
45                                         spannode = document.createElement( 'span' );
46                                         spannode.className = 'highlight';
47                                         // shave off the characters preceding the matched text
48                                         middlebit = node.splitText( pos );
49                                         // shave off any unmatched text off the end
50                                         middlebit.splitText( match[ 0 ].length - match[ 1 ].length );
51                                         // clone for appending to our span
52                                         middleclone = middlebit.cloneNode( true );
53                                         // append the matched text node to the span
54                                         spannode.appendChild( middleclone );
55                                         // replace the matched node, with our span-wrapped clone of the matched node
56                                         middlebit.parentNode.replaceChild( spannode, middlebit );
57                                 }
58                         } else if (
59                                 node.nodeType === Node.ELEMENT_NODE &&
60                                 // element with childnodes, and not a script, style or an element we created
61                                 node.childNodes &&
62                                 !/(script|style)/i.test( node.tagName ) &&
63                                 !(
64                                         node.tagName.toLowerCase() === 'span' &&
65                                         node.className.match( /\bhighlight/ )
66                                 )
67                         ) {
68                                 for ( i = 0; i < node.childNodes.length; ++i ) {
69                                         // call the highlight function for each child node
70                                         $.highlightText.innerHighlight( node.childNodes[ i ], pat );
71                                 }
72                         }
73                 }
74         };
76         /**
77          * Highlight certain text in current nodes (by wrapping it in `<span class="highlight">...</span>`).
78          *
79          * @param {string} matchString String to match
80          * @param {Object} [options]
81          * @param {string} [options.method='splitAndHighlight'] Method of matching to use, one of:
82          *   - 'splitAndHighlight': Split `matchString` on spaces, then match each word separately.
83          *   - 'prefixHighlight': Match `matchString` at the beginning of text only.
84          * @return {jQuery}
85          * @chainable
86          */
87         $.fn.highlightText = function ( matchString, options ) {
88                 options = options || {};
89                 options.method = options.method || 'splitAndHighlight';
90                 return this.each( function () {
91                         var $el = $( this );
92                         $el.data( 'highlightText', { originalText: $el.text() } );
93                         $.highlightText[ options.method ]( this, matchString );
94                 } );
95         };
97 }( jQuery, mediaWiki ) );