(bug 28978) jquery.autoEllipsis cache doesn't take position into account, leads to...
[lhc/web/wiklou.git] / resources / jquery / jquery.autoEllipsis.js
1 /**
2 * Plugin that automatically truncates the plain text contents of an element and adds an ellipsis
3 */
4 ( function( $ ) {
5
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 = { };
10
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;
35
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 console.log("YAY CACHE HIT");
82 return;
83 }
84 }
85
86 if ( $trimmableText.width() + pw > w ) {
87 switch ( options.position ) {
88 case 'right':
89 // Use binary search-like technique for efficiency
90 var l = 0, r = trimmableText.length;
91 do {
92 var m = Math.ceil( ( l + r ) / 2 );
93 $trimmableText.text( trimmableText.substr( 0, m ) + '...' );
94 if ( $trimmableText.width() + pw > w ) {
95 // Text is too long
96 r = m - 1;
97 } else {
98 l = m;
99 }
100 } while ( l < r );
101 $trimmableText.text( trimmableText.substr( 0, l ) + '...' );
102 break;
103 case 'center':
104 // TODO: Use binary search like for 'right'
105 var i = [Math.round( trimmableText.length / 2 ), Math.round( trimmableText.length / 2 )];
106 var side = 1; // Begin with making the end shorter
107 while ( $trimmableText.outerWidth() + pw > w && i[0] > 0 ) {
108 $trimmableText.text( trimmableText.substr( 0, i[0] ) + '...' + trimmableText.substr( i[1] ) );
109 // Alternate between trimming the end and begining
110 if ( side === 0 ) {
111 // Make the begining shorter
112 i[0]--;
113 side = 1;
114 } else {
115 // Make the end shorter
116 i[1]++;
117 side = 0;
118 }
119 }
120 break;
121 case 'left':
122 // TODO: Use binary search like for 'right'
123 var r = 0;
124 while ( $trimmableText.outerWidth() + pw > w && r < trimmableText.length ) {
125 $trimmableText.text( '...' + trimmableText.substr( r ) );
126 r++;
127 }
128 break;
129 }
130 }
131 if ( options.tooltip ) {
132 $container.attr( 'title', text );
133 }
134 if ( options.matchText ) {
135 $container.highlightText( options.matchText );
136 matchTextCache[text][options.matchText][w][options.position] = $container.html();
137 } else {
138 cache[text][w][options.position] = $container.html();
139 }
140
141 } );
142 };
143
144 } )( jQuery );