jshint: resources/jquery/*
[mediawiki.git] / resources / jquery / jquery.autoEllipsis.js
blob23ba074052a3b4f783362d4f3a5ac0c588f6d100
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 separate 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 $container, $trimmableText,
21                         text, trimmableText, w, pw,
22                         l, r, i, side,
23                         $el = $(this);
24                 if ( options.restoreText ) {
25                         if ( !$el.data( 'autoEllipsis.originalText' ) ) {
26                                 $el.data( 'autoEllipsis.originalText', $el.text() );
27                         } else {
28                                 $el.text( $el.data( 'autoEllipsis.originalText' ) );
29                         }
30                 }
32                 // container element - used for measuring against
33                 $container = $el;
35                 // trimmable text element - only the text within this element will be trimmed
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                 }
47                 text = $container.text();
48                 trimmableText = $trimmableText.text();
49                 w = $container.width();
50                 pw = 0;
52                 // Try cache
53                 if ( options.matchText ) {
54                         if ( !( text in matchTextCache ) ) {
55                                 matchTextCache[text] = {};
56                         }
57                         if ( !( options.matchText in matchTextCache[text] ) ) {
58                                 matchTextCache[text][options.matchText] = {};
59                         }
60                         if ( !( w in matchTextCache[text][options.matchText] ) ) {
61                                 matchTextCache[text][options.matchText][w] = {};
62                         }
63                         if ( options.position in matchTextCache[text][options.matchText][w] ) {
64                                 $container.html( matchTextCache[text][options.matchText][w][options.position] );
65                                 if ( options.tooltip ) {
66                                         $container.attr( 'title', text );
67                                 }
68                                 return;
69                         }
70                 } else {
71                         if ( !( text in cache ) ) {
72                                 cache[text] = {};
73                         }
74                         if ( !( w in cache[text] ) ) {
75                                 cache[text][w] = {};
76                         }
77                         if ( options.position in cache[text][w] ) {
78                                 $container.html( cache[text][w][options.position] );
79                                 if ( options.tooltip ) {
80                                         $container.attr( 'title', text );
81                                 }
82                                 return;
83                         }
84                 }
86                 if ( $trimmableText.width() + pw > w ) {
87                         switch ( options.position ) {
88                                 case 'right':
89                                         // Use binary search-like technique for efficiency
90                                         l = 0;
91                                         r = trimmableText.length;
92                                         do {
93                                                 var m = Math.ceil( ( l + r ) / 2 );
94                                                 $trimmableText.text( trimmableText.substr( 0, m ) + '...' );
95                                                 if ( $trimmableText.width() + pw > w ) {
96                                                         // Text is too long
97                                                         r = m - 1;
98                                                 } else {
99                                                         l = m;
100                                                 }
101                                         } while ( l < r );
102                                         $trimmableText.text( trimmableText.substr( 0, l ) + '...' );
103                                         break;
104                                 case 'center':
105                                         // TODO: Use binary search like for 'right'
106                                         i = [Math.round( trimmableText.length / 2 ), Math.round( trimmableText.length / 2 )];
107                                         // Begin with making the end shorter
108                                         side = 1;
109                                         while ( $trimmableText.outerWidth() + pw > w && i[0] > 0 ) {
110                                                 $trimmableText.text( trimmableText.substr( 0, i[0] ) + '...' + trimmableText.substr( i[1] ) );
111                                                 // Alternate between trimming the end and begining
112                                                 if ( side === 0 ) {
113                                                         // Make the begining shorter
114                                                         i[0]--;
115                                                         side = 1;
116                                                 } else {
117                                                         // Make the end shorter
118                                                         i[1]++;
119                                                         side = 0;
120                                                 }
121                                         }
122                                         break;
123                                 case 'left':
124                                         // TODO: Use binary search like for 'right'
125                                         r = 0;
126                                         while ( $trimmableText.outerWidth() + pw > w && r < trimmableText.length ) {
127                                                 $trimmableText.text( '...' + trimmableText.substr( r ) );
128                                                 r++;
129                                         }
130                                         break;
131                         }
132                 }
133                 if ( options.tooltip ) {
134                         $container.attr( 'title', text );
135                 }
136                 if ( options.matchText ) {
137                         $container.highlightText( options.matchText );
138                         matchTextCache[text][options.matchText][w][options.position] = $container.html();
139                 } else {
140                         cache[text][w][options.position] = $container.html();
141                 }
143         } );
146 }( jQuery ) );