Merge "Add ss_active_users in SiteStats::isSane"
[mediawiki.git] / resources / jquery / jquery.autoEllipsis.js
blob49a932a1e6a53bb42cac7c44565cfc928bdf4c4e
1 /**
2  * Plugin that automatically truncates the plain text contents of an element
3  * and adds an ellipsis.
4  */
5 ( function ( $ ) {
7 var
8         // Cache ellipsed substrings for every string-width-position combination
9         cache = {},
11         // Use a separate cache when match highlighting is enabled
12         matchTextCache = {};
14 $.fn.autoEllipsis = function ( options ) {
15         options = $.extend( {
16                 position: 'center',
17                 tooltip: false,
18                 restoreText: false,
19                 hasSpan: false,
20                 matchText: null
21         }, options );
23         return this.each( function () {
24                 var $trimmableText,
25                         text, trimmableText, w, pw,
26                         l, r, i, side, m,
27                         // container element - used for measuring against
28                         $container = $(this);
30                 if ( options.restoreText ) {
31                         if ( !$container.data( 'autoEllipsis.originalText' ) ) {
32                                 $container.data( 'autoEllipsis.originalText', $container.text() );
33                         } else {
34                                 $container.text( $container.data( 'autoEllipsis.originalText' ) );
35                         }
36                 }
38                 // trimmable text element - only the text within this element will be trimmed
39                 if ( options.hasSpan ) {
40                         $trimmableText = $container.children( options.selector );
41                 } else {
42                         $trimmableText = $( '<span>' )
43                                 .css( 'whiteSpace', 'nowrap' )
44                                 .text( $container.text() );
45                         $container
46                                 .empty()
47                                 .append( $trimmableText );
48                 }
50                 text = $container.text();
51                 trimmableText = $trimmableText.text();
52                 w = $container.width();
53                 pw = 0;
55                 // Try cache
56                 if ( options.matchText ) {
57                         if ( !( text in matchTextCache ) ) {
58                                 matchTextCache[text] = {};
59                         }
60                         if ( !( options.matchText in matchTextCache[text] ) ) {
61                                 matchTextCache[text][options.matchText] = {};
62                         }
63                         if ( !( w in matchTextCache[text][options.matchText] ) ) {
64                                 matchTextCache[text][options.matchText][w] = {};
65                         }
66                         if ( options.position in matchTextCache[text][options.matchText][w] ) {
67                                 $container.html( matchTextCache[text][options.matchText][w][options.position] );
68                                 if ( options.tooltip ) {
69                                         $container.attr( 'title', text );
70                                 }
71                                 return;
72                         }
73                 } else {
74                         if ( !( text in cache ) ) {
75                                 cache[text] = {};
76                         }
77                         if ( !( w in cache[text] ) ) {
78                                 cache[text][w] = {};
79                         }
80                         if ( options.position in cache[text][w] ) {
81                                 $container.html( cache[text][w][options.position] );
82                                 if ( options.tooltip ) {
83                                         $container.attr( 'title', text );
84                                 }
85                                 return;
86                         }
87                 }
89                 if ( $trimmableText.width() + pw > w ) {
90                         switch ( options.position ) {
91                                 case 'right':
92                                         // Use binary search-like technique for efficiency
93                                         l = 0;
94                                         r = trimmableText.length;
95                                         do {
96                                                 m = Math.ceil( ( l + r ) / 2 );
97                                                 $trimmableText.text( trimmableText.substr( 0, m ) + '...' );
98                                                 if ( $trimmableText.width() + pw > w ) {
99                                                         // Text is too long
100                                                         r = m - 1;
101                                                 } else {
102                                                         l = m;
103                                                 }
104                                         } while ( l < r );
105                                         $trimmableText.text( trimmableText.substr( 0, l ) + '...' );
106                                         break;
107                                 case 'center':
108                                         // TODO: Use binary search like for 'right'
109                                         i = [Math.round( trimmableText.length / 2 ), Math.round( trimmableText.length / 2 )];
110                                         // Begin with making the end shorter
111                                         side = 1;
112                                         while ( $trimmableText.outerWidth() + pw > w && i[0] > 0 ) {
113                                                 $trimmableText.text( trimmableText.substr( 0, i[0] ) + '...' + trimmableText.substr( i[1] ) );
114                                                 // Alternate between trimming the end and begining
115                                                 if ( side === 0 ) {
116                                                         // Make the begining shorter
117                                                         i[0]--;
118                                                         side = 1;
119                                                 } else {
120                                                         // Make the end shorter
121                                                         i[1]++;
122                                                         side = 0;
123                                                 }
124                                         }
125                                         break;
126                                 case 'left':
127                                         // TODO: Use binary search like for 'right'
128                                         r = 0;
129                                         while ( $trimmableText.outerWidth() + pw > w && r < trimmableText.length ) {
130                                                 $trimmableText.text( '...' + trimmableText.substr( r ) );
131                                                 r++;
132                                         }
133                                         break;
134                         }
135                 }
136                 if ( options.tooltip ) {
137                         $container.attr( 'title', text );
138                 }
139                 if ( options.matchText ) {
140                         $container.highlightText( options.matchText );
141                         matchTextCache[text][options.matchText][w][options.position] = $container.html();
142                 } else {
143                         cache[text][w][options.position] = $container.html();
144                 }
146         } );
149 }( jQuery ) );