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