]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blobdiff - resources/src/jquery/jquery.autoEllipsis.js
MediaWiki 1.30.2
[autoinstallsdev/mediawiki.git] / resources / src / jquery / jquery.autoEllipsis.js
diff --git a/resources/src/jquery/jquery.autoEllipsis.js b/resources/src/jquery/jquery.autoEllipsis.js
new file mode 100644 (file)
index 0000000..8716b69
--- /dev/null
@@ -0,0 +1,171 @@
+/**
+ * @class jQuery.plugin.autoEllipsis
+ */
+( function ( $ ) {
+
+       var
+               // Cache ellipsed substrings for every string-width-position combination
+               cache = {},
+
+               // Use a separate cache when match highlighting is enabled
+               matchTextCache = {};
+
+       // Due to <https://github.com/jscs-dev/jscs-jsdoc/issues/136>
+       // jscs:disable jsDoc
+       /**
+        * Automatically truncate the plain text contents of an element and add an ellipsis
+        *
+        * @param {Object} options
+        * @param {'left'|'center'|'right'} [options.position='center'] Where to remove text.
+        * @param {boolean} [options.tooltip=false] Whether to show a tooltip with the remainder
+        * of the text.
+        * @param {boolean} [options.restoreText=false] Whether to save the text for restoring
+        * later.
+        * @param {boolean} [options.hasSpan=false] Whether the element is already a container,
+        * or if the library should create a new container for it.
+        * @param {string|null} [options.matchText=null] Text to highlight, e.g. search terms.
+        * @return {jQuery}
+        * @chainable
+        */
+       $.fn.autoEllipsis = function ( options ) {
+               options = $.extend( {
+                       position: 'center',
+                       tooltip: false,
+                       restoreText: false,
+                       hasSpan: false,
+                       matchText: null
+               }, options );
+
+               return this.each( function () {
+                       var $trimmableText,
+                               text, trimmableText, w, pw,
+                               l, r, i, side, m,
+                               // container element - used for measuring against
+                               $container = $( this );
+
+                       if ( options.restoreText ) {
+                               if ( !$container.data( 'autoEllipsis.originalText' ) ) {
+                                       $container.data( 'autoEllipsis.originalText', $container.text() );
+                               } else {
+                                       $container.text( $container.data( 'autoEllipsis.originalText' ) );
+                               }
+                       }
+
+                       // trimmable text element - only the text within this element will be trimmed
+                       if ( options.hasSpan ) {
+                               $trimmableText = $container.children( options.selector );
+                       } else {
+                               $trimmableText = $( '<span>' )
+                                       .css( 'whiteSpace', 'nowrap' )
+                                       .text( $container.text() );
+                               $container
+                                       .empty()
+                                       .append( $trimmableText );
+                       }
+
+                       text = $container.text();
+                       trimmableText = $trimmableText.text();
+                       w = $container.width();
+                       pw = 0;
+
+                       // Try cache
+                       if ( options.matchText ) {
+                               if ( !( text in matchTextCache ) ) {
+                                       matchTextCache[ text ] = {};
+                               }
+                               if ( !( options.matchText in matchTextCache[ text ] ) ) {
+                                       matchTextCache[ text ][ options.matchText ] = {};
+                               }
+                               if ( !( w in matchTextCache[ text ][ options.matchText ] ) ) {
+                                       matchTextCache[ text ][ options.matchText ][ w ] = {};
+                               }
+                               if ( options.position in matchTextCache[ text ][ options.matchText ][ w ] ) {
+                                       $container.html( matchTextCache[ text ][ options.matchText ][ w ][ options.position ] );
+                                       if ( options.tooltip ) {
+                                               $container.attr( 'title', text );
+                                       }
+                                       return;
+                               }
+                       } else {
+                               if ( !( text in cache ) ) {
+                                       cache[ text ] = {};
+                               }
+                               if ( !( w in cache[ text ] ) ) {
+                                       cache[ text ][ w ] = {};
+                               }
+                               if ( options.position in cache[ text ][ w ] ) {
+                                       $container.html( cache[ text ][ w ][ options.position ] );
+                                       if ( options.tooltip ) {
+                                               $container.attr( 'title', text );
+                                       }
+                                       return;
+                               }
+                       }
+
+                       if ( $trimmableText.width() + pw > w ) {
+                               switch ( options.position ) {
+                                       case 'right':
+                                               // Use binary search-like technique for efficiency
+                                               l = 0;
+                                               r = trimmableText.length;
+                                               do {
+                                                       m = Math.ceil( ( l + r ) / 2 );
+                                                       $trimmableText.text( trimmableText.slice( 0, m ) + '...' );
+                                                       if ( $trimmableText.width() + pw > w ) {
+                                                               // Text is too long
+                                                               r = m - 1;
+                                                       } else {
+                                                               l = m;
+                                                       }
+                                               } while ( l < r );
+                                               $trimmableText.text( trimmableText.slice( 0, l ) + '...' );
+                                               break;
+                                       case 'center':
+                                               // TODO: Use binary search like for 'right'
+                                               i = [ Math.round( trimmableText.length / 2 ), Math.round( trimmableText.length / 2 ) ];
+                                               // Begin with making the end shorter
+                                               side = 1;
+                                               while ( $trimmableText.outerWidth() + pw > w && i[ 0 ] > 0 ) {
+                                                       $trimmableText.text( trimmableText.slice( 0, i[ 0 ] ) + '...' + trimmableText.slice( i[ 1 ] ) );
+                                                       // Alternate between trimming the end and begining
+                                                       if ( side === 0 ) {
+                                                               // Make the begining shorter
+                                                               i[ 0 ]--;
+                                                               side = 1;
+                                                       } else {
+                                                               // Make the end shorter
+                                                               i[ 1 ]++;
+                                                               side = 0;
+                                                       }
+                                               }
+                                               break;
+                                       case 'left':
+                                               // TODO: Use binary search like for 'right'
+                                               r = 0;
+                                               while ( $trimmableText.outerWidth() + pw > w && r < trimmableText.length ) {
+                                                       $trimmableText.text( '...' + trimmableText.slice( r ) );
+                                                       r++;
+                                               }
+                                               break;
+                               }
+                       }
+                       if ( options.tooltip ) {
+                               $container.attr( 'title', text );
+                       }
+                       if ( options.matchText ) {
+                               $container.highlightText( options.matchText );
+                               matchTextCache[ text ][ options.matchText ][ w ][ options.position ] = $container.html();
+                       } else {
+                               cache[ text ][ w ][ options.position ] = $container.html();
+                       }
+
+               } );
+       };
+       // jscs:enable jsDoc
+
+       /**
+        * @class jQuery
+        * @mixins jQuery.plugin.autoEllipsis
+        */
+
+}( jQuery ) );