/* * jQuery dotdotdot * * Copyright (c) Fred Heusschen * www.frebsite.nl * * Plugin website: * dotdotdot.frebsite.nl * * Licensed under the MIT license. * http://en.wikipedia.org/wiki/MIT_License */ (function( $, undef ) { if ( $.fn.dotdotdot ) { return; } $.fn.dotdotdot = function( o ) { if ( this.length == 0 ) { $.fn.dotdotdot.debug( 'No element found for "' + this.selector + '".' ); return this; } if ( this.length > 1 ) { return this.each( function() { $(this).dotdotdot( o ); } ); } var $dot = this; if ( $dot.data( 'dotdotdot' ) ) { $dot.trigger( 'destroy.dot' ); } $dot.data( 'dotdotdot-style', $dot.attr( 'style' ) || '' ); $dot.css( 'word-wrap', 'break-word' ); if ($dot.css( 'white-space' ) === 'nowrap') { $dot.css( 'white-space', 'normal' ); } $dot.bind_events = function() { $dot.bind( 'update.dot', function( e, c ) { e.preventDefault(); e.stopPropagation(); opts.maxHeight = ( typeof opts.height == 'number' ) ? opts.height : getTrueInnerHeight( $dot ); opts.maxHeight += opts.tolerance; if ( typeof c != 'undefined' ) { if ( typeof c == 'string' || ('nodeType' in c && c.nodeType === 1) ) { c = $('
').append( c ).contents(); } if ( c instanceof $ ) { orgContent = c; } } $inr = $dot.wrapInner( '
' ).children(); $inr.contents() .detach() .end() .append( orgContent.clone( true ) ) .find( 'br' ) .replaceWith( '
' ) .end() .css({ 'height' : 'auto', 'width' : 'auto', 'border' : 'none', 'padding' : 0, 'margin' : 0 }); var after = false, trunc = false; if ( conf.afterElement ) { after = conf.afterElement.clone( true ); after.show(); conf.afterElement.detach(); } if ( test( $inr, opts ) ) { if ( opts.wrap == 'children' ) { trunc = children( $inr, opts, after ); } else { trunc = ellipsis( $inr, $dot, $inr, opts, after ); } } $inr.replaceWith( $inr.contents() ); $inr = null; if ( $.isFunction( opts.callback ) ) { opts.callback.call( $dot[ 0 ], trunc, orgContent ); } conf.isTruncated = trunc; return trunc; } ).bind( 'isTruncated.dot', function( e, fn ) { e.preventDefault(); e.stopPropagation(); if ( typeof fn == 'function' ) { fn.call( $dot[ 0 ], conf.isTruncated ); } return conf.isTruncated; } ).bind( 'originalContent.dot', function( e, fn ) { e.preventDefault(); e.stopPropagation(); if ( typeof fn == 'function' ) { fn.call( $dot[ 0 ], orgContent ); } return orgContent; } ).bind( 'destroy.dot', function( e ) { e.preventDefault(); e.stopPropagation(); $dot.unwatch() .unbind_events() .contents() .detach() .end() .append( orgContent ) .attr( 'style', $dot.data( 'dotdotdot-style' ) || '' ) .data( 'dotdotdot', false ); } ); return $dot; }; // /bind_events $dot.unbind_events = function() { $dot.unbind('.dot'); return $dot; }; // /unbind_events $dot.watch = function() { $dot.unwatch(); if ( opts.watch == 'window' ) { var $window = $(window), _wWidth = $window.width(), _wHeight = $window.height(); $window.bind( 'resize.dot' + conf.dotId, function() { if ( _wWidth != $window.width() || _wHeight != $window.height() || !opts.windowResizeFix ) { _wWidth = $window.width(); _wHeight = $window.height(); if ( watchInt ) { clearInterval( watchInt ); } watchInt = setTimeout( function() { $dot.trigger( 'update.dot' ); }, 0 ); } } ); } else { watchOrg = getSizes( $dot ); watchInt = setInterval( function() { if ( $dot.is( ':visible' ) ) { var watchNew = getSizes( $dot ); if ( watchOrg.width != watchNew.width || watchOrg.height != watchNew.height ) { $dot.trigger( 'update.dot' ); watchOrg = watchNew; } } }, 0 ); } return $dot; }; $dot.unwatch = function() { $(window).unbind( 'resize.dot' + conf.dotId ); if ( watchInt ) { clearInterval( watchInt ); } return $dot; }; var orgContent = $dot.contents(), opts = $.extend( true, {}, $.fn.dotdotdot.defaults, o ), conf = {}, watchOrg = {}, watchInt = null, $inr = null; if ( !( opts.lastCharacter.remove instanceof Array ) ) { opts.lastCharacter.remove = $.fn.dotdotdot.defaultArrays.lastCharacter.remove; } if ( !( opts.lastCharacter.noEllipsis instanceof Array ) ) { opts.lastCharacter.noEllipsis = $.fn.dotdotdot.defaultArrays.lastCharacter.noEllipsis; } conf.afterElement = getElement( opts.after, $dot ); conf.isTruncated = false; conf.dotId = dotId++; $dot.data( 'dotdotdot', true ) .bind_events() .trigger( 'update.dot' ); if ( opts.watch ) { $dot.watch(); } return $dot; }; // public $.fn.dotdotdot.defaults = { 'ellipsis' : '... ', 'wrap' : 'word', 'fallbackToLetter' : true, 'lastCharacter' : {}, 'tolerance' : 0, 'callback' : null, 'after' : null, 'height' : null, 'watch' : false, 'windowResizeFix' : true }; $.fn.dotdotdot.defaultArrays = { 'lastCharacter' : { 'remove' : [ ' ', '\u3000', ',', ';', '.', '!', '?' ], 'noEllipsis' : [] } }; $.fn.dotdotdot.debug = function( msg ) {}; // private var dotId = 1; function children( $elem, o, after ) { var $elements = $elem.children(), isTruncated = false; $elem.empty(); for ( var a = 0, l = $elements.length; a < l; a++ ) { var $e = $elements.eq( a ); $elem.append( $e ); if ( after ) { $elem.append( after ); } if ( test( $elem, o ) ) { $e.remove(); isTruncated = true; break; } else { if ( after ) { after.detach(); } } } return isTruncated; } function ellipsis( $elem, $d, $i, o, after ) { var isTruncated = false; // Don't put the ellipsis directly inside these elements var notx = 'a table, thead, tbody, tfoot, tr, col, colgroup, object, embed, param, ol, ul, dl, blockquote, select, optgroup, option, textarea, script, style'; // Don't remove these elements even if they are after the ellipsis var noty = 'script, .dotdotdot-keep'; $elem .contents() .detach() .each( function() { var e = this, $e = $(e); if ( typeof e == 'undefined' || ( e.nodeType == 3 && $.trim( e.data ).length == 0 ) ) { return true; } else if ( $e.is( noty ) ) { $elem.append( $e ); } else if ( isTruncated ) { return true; } else { $elem.append( $e ); if ( after && !$e.is( o.after ) && !$e.find( o.after ).length ) { $elem[ $elem.is( notx ) ? 'after' : 'append' ]( after ); } if ( test( $i, o ) ) { if ( e.nodeType == 3 ) // node is TEXT { isTruncated = ellipsisElement( $e, $d, $i, o, after ); } else { isTruncated = ellipsis( $e, $d, $i, o, after ); } if ( !isTruncated ) { $e.detach(); isTruncated = true; } } if ( !isTruncated ) { if ( after ) { after.detach(); } } } } ); return isTruncated; } function ellipsisElement( $e, $d, $i, o, after ) { var e = $e[ 0 ]; if ( !e ) { return false; } var txt = getTextContent( e ), space = ( txt.indexOf(' ') !== -1 ) ? ' ' : '\u3000', separator = ( o.wrap == 'letter' ) ? '' : space, textArr = txt.split( separator ), position = -1, midPos = -1, startPos = 0, endPos = textArr.length - 1; // Only one word if ( o.fallbackToLetter && startPos == 0 && endPos == 0 ) { separator = ''; textArr = txt.split( separator ); endPos = textArr.length - 1; } while ( startPos <= endPos && !( startPos == 0 && endPos == 0 ) ) { var m = Math.floor( ( startPos + endPos ) / 2 ); if ( m == midPos ) { break; } midPos = m; setTextContent( e, textArr.slice( 0, midPos + 1 ).join( separator ) + o.ellipsis ); if ( !test( $i, o ) ) { position = midPos; startPos = midPos; } else { endPos = midPos; // Fallback to letter if (o.fallbackToLetter && startPos == 0 && endPos == 0 ) { separator = ''; textArr = textArr[ 0 ].split( separator ); position = -1; midPos = -1; startPos = 0; endPos = textArr.length - 1; } } } if ( position != -1 && !( textArr.length == 1 && textArr[ 0 ].length == 0 ) ) { txt = addEllipsis( textArr.slice( 0, position + 1 ).join( separator ), o ); setTextContent( e, txt ); } else { var $w = $e.parent(); $e.detach(); var afterLength = ( after && after.closest($w).length ) ? after.length : 0; if ( $w.contents().length > afterLength ) { e = findLastTextNode( $w.contents().eq( -1 - afterLength ), $d ); } else { e = findLastTextNode( $w, $d, true ); if ( !afterLength ) { $w.detach(); } } if ( e ) { txt = addEllipsis( getTextContent( e ), o ); setTextContent( e, txt ); if ( afterLength && after ) { $(e).parent().append( after ); } } } return true; } function test( $i, o ) { return $i.innerHeight() > o.maxHeight; } function addEllipsis( txt, o ) { while( $.inArray( txt.slice( -1 ), o.lastCharacter.remove ) > -1 ) { txt = txt.slice( 0, -1 ); } if ( $.inArray( txt.slice( -1 ), o.lastCharacter.noEllipsis ) < 0 ) { txt += o.ellipsis; } return txt; } function getSizes( $d ) { return { 'width' : $d.innerWidth(), 'height': $d.innerHeight() }; } function setTextContent( e, content ) { if ( e.innerText ) { e.innerText = content; } else if ( e.nodeValue ) { e.nodeValue = content; } else if (e.textContent) { e.textContent = content; } } function getTextContent( e ) { if ( e.innerText ) { return e.innerText; } else if ( e.nodeValue ) { return e.nodeValue; } else if ( e.textContent ) { return e.textContent; } else { return ""; } } function getPrevNode( n ) { do { n = n.previousSibling; } while ( n && n.nodeType !== 1 && n.nodeType !== 3 ); return n; } function findLastTextNode( $el, $top, excludeCurrent ) { var e = $el && $el[ 0 ], p; if ( e ) { if ( !excludeCurrent ) { if ( e.nodeType === 3 ) { return e; } if ( $.trim( $el.text() ) ) { return findLastTextNode( $el.contents().last(), $top ); } } p = getPrevNode( e ); while ( !p ) { $el = $el.parent(); if ( $el.is( $top ) || !$el.length ) { return false; } p = getPrevNode( $el[0] ); } if ( p ) { return findLastTextNode( $(p), $top ); } } return false; } function getElement( e, $i ) { if ( !e ) { return false; } if ( typeof e === 'string' ) { e = $(e, $i); return ( e.length ) ? e : false; } return !e.jquery ? false : e; } function getTrueInnerHeight( $el ) { var h = $el.innerHeight(), a = [ 'paddingTop', 'paddingBottom' ]; for ( var z = 0, l = a.length; z < l; z++ ) { var m = parseInt( $el.css( a[ z ] ), 10 ); if ( isNaN( m ) ) { m = 0; } h -= m; } return h; } // override jQuery.html var _orgHtml = $.fn.html; $.fn.html = function( str ) { if ( str != undef && !$.isFunction( str ) && this.data( 'dotdotdot' ) ) { return this.trigger( 'update', [ str ] ); } return _orgHtml.apply( this, arguments ); }; // override jQuery.text var _orgText = $.fn.text; $.fn.text = function( str ) { if ( str != undef && !$.isFunction( str ) && this.data( 'dotdotdot' ) ) { str = $( '
' ).text( str ).html(); return this.trigger( 'update', [ str ] ); } return _orgText.apply( this, arguments ); }; })( jQuery );