Merge "Remove not used private member variable mParserWarnings from OutputPage"
[mediawiki.git] / resources / src / jquery / jquery.autoEllipsis.js
blobfd7e8d1e39656655c9e613d3f05f8d16c89aab48
1 /**
2  * @class jQuery.plugin.autoEllipsis
3  */
4 ( function ( $ ) {
6 var
7         // Cache ellipsed substrings for every string-width-position combination
8         cache = {},
10         // Use a separate cache when match highlighting is enabled
11         matchTextCache = {};
13 // Due to <https://github.com/jscs-dev/jscs-jsdoc/issues/136>
14 // jscs:disable jsDoc
15 /**
16  * Automatically truncate the plain text contents of an element and add an ellipsis
17  *
18  * @param {Object} options
19  * @param {'left'|'center'|'right'} [options.position='center'] Where to remove text.
20  * @param {boolean} [options.tooltip=false] Whether to show a tooltip with the remainder
21  * of the text.
22  * @param {boolean} [options.restoreText=false] Whether to save the text for restoring
23  * later.
24  * @param {boolean} [options.hasSpan=false] Whether the element is already a container,
25  * or if the library should create a new container for it.
26  * @param {string|null} [options.matchText=null] Text to highlight, e.g. search terms.
27  * @return {jQuery}
28  * @chainable
29  */
30 $.fn.autoEllipsis = function ( options ) {
31         options = $.extend( {
32                 position: 'center',
33                 tooltip: false,
34                 restoreText: false,
35                 hasSpan: false,
36                 matchText: null
37         }, options );
39         return this.each( function () {
40                 var $trimmableText,
41                         text, trimmableText, w, pw,
42                         l, r, i, side, m,
43                         // container element - used for measuring against
44                         $container = $( this );
46                 if ( options.restoreText ) {
47                         if ( !$container.data( 'autoEllipsis.originalText' ) ) {
48                                 $container.data( 'autoEllipsis.originalText', $container.text() );
49                         } else {
50                                 $container.text( $container.data( 'autoEllipsis.originalText' ) );
51                         }
52                 }
54                 // trimmable text element - only the text within this element will be trimmed
55                 if ( options.hasSpan ) {
56                         $trimmableText = $container.children( options.selector );
57                 } else {
58                         $trimmableText = $( '<span>' )
59                                 .css( 'whiteSpace', 'nowrap' )
60                                 .text( $container.text() );
61                         $container
62                                 .empty()
63                                 .append( $trimmableText );
64                 }
66                 text = $container.text();
67                 trimmableText = $trimmableText.text();
68                 w = $container.width();
69                 pw = 0;
71                 // Try cache
72                 if ( options.matchText ) {
73                         if ( !( text in matchTextCache ) ) {
74                                 matchTextCache[ text ] = {};
75                         }
76                         if ( !( options.matchText in matchTextCache[ text ] ) ) {
77                                 matchTextCache[ text ][ options.matchText ] = {};
78                         }
79                         if ( !( w in matchTextCache[ text ][ options.matchText ] ) ) {
80                                 matchTextCache[ text ][ options.matchText ][ w ] = {};
81                         }
82                         if ( options.position in matchTextCache[ text ][ options.matchText ][ w ] ) {
83                                 $container.html( matchTextCache[ text ][ options.matchText ][ w ][ options.position ] );
84                                 if ( options.tooltip ) {
85                                         $container.attr( 'title', text );
86                                 }
87                                 return;
88                         }
89                 } else {
90                         if ( !( text in cache ) ) {
91                                 cache[ text ] = {};
92                         }
93                         if ( !( w in cache[ text ] ) ) {
94                                 cache[ text ][ w ] = {};
95                         }
96                         if ( options.position in cache[ text ][ w ] ) {
97                                 $container.html( cache[ text ][ w ][ options.position ] );
98                                 if ( options.tooltip ) {
99                                         $container.attr( 'title', text );
100                                 }
101                                 return;
102                         }
103                 }
105                 if ( $trimmableText.width() + pw > w ) {
106                         switch ( options.position ) {
107                                 case 'right':
108                                         // Use binary search-like technique for efficiency
109                                         l = 0;
110                                         r = trimmableText.length;
111                                         do {
112                                                 m = Math.ceil( ( l + r ) / 2 );
113                                                 $trimmableText.text( trimmableText.slice( 0, m ) + '...' );
114                                                 if ( $trimmableText.width() + pw > w ) {
115                                                         // Text is too long
116                                                         r = m - 1;
117                                                 } else {
118                                                         l = m;
119                                                 }
120                                         } while ( l < r );
121                                         $trimmableText.text( trimmableText.slice( 0, l ) + '...' );
122                                         break;
123                                 case 'center':
124                                         // TODO: Use binary search like for 'right'
125                                         i = [ Math.round( trimmableText.length / 2 ), Math.round( trimmableText.length / 2 ) ];
126                                         // Begin with making the end shorter
127                                         side = 1;
128                                         while ( $trimmableText.outerWidth() + pw > w && i[ 0 ] > 0 ) {
129                                                 $trimmableText.text( trimmableText.slice( 0, i[ 0 ] ) + '...' + trimmableText.slice( i[ 1 ] ) );
130                                                 // Alternate between trimming the end and begining
131                                                 if ( side === 0 ) {
132                                                         // Make the begining shorter
133                                                         i[ 0 ]--;
134                                                         side = 1;
135                                                 } else {
136                                                         // Make the end shorter
137                                                         i[ 1 ]++;
138                                                         side = 0;
139                                                 }
140                                         }
141                                         break;
142                                 case 'left':
143                                         // TODO: Use binary search like for 'right'
144                                         r = 0;
145                                         while ( $trimmableText.outerWidth() + pw > w && r < trimmableText.length ) {
146                                                 $trimmableText.text( '...' + trimmableText.slice( r ) );
147                                                 r++;
148                                         }
149                                         break;
150                         }
151                 }
152                 if ( options.tooltip ) {
153                         $container.attr( 'title', text );
154                 }
155                 if ( options.matchText ) {
156                         $container.highlightText( options.matchText );
157                         matchTextCache[ text ][ options.matchText ][ w ][ options.position ] = $container.html();
158                 } else {
159                         cache[ text ][ w ][ options.position ] = $container.html();
160                 }
162         } );
164 // jscs:enable jsDoc
167  * @class jQuery
168  * @mixins jQuery.plugin.autoEllipsis
169  */
171 }( jQuery ) );