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