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