diff --git a/bower.json b/bower.json index bee6df7..36227b4 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name" : "jQuery.dotdotdot", - "version" : "3.1.0", + "version" : "3.2.0", "main" : "dist/jquery.dotdotdot.js", "authors" : "Fred Heusschen ", "license" : "CC-BY-NC-4.0", diff --git a/composer.json b/composer.json index 8fcfa4a..bb0f446 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name" : "jQuery.dotdotdot", - "version" : "3.1.0", + "version" : "3.2.0", "authors" : "Fred Heusschen ", "license" : "CC-BY-NC-4.0", "description" : "Dotdotdot is an advanced jQuery plugin for truncating multiple line content with an ellipsis.", diff --git a/dist/jquery.dotdotdot.js b/dist/jquery.dotdotdot.js index 17dc825..b1068a3 100644 --- a/dist/jquery.dotdotdot.js +++ b/dist/jquery.dotdotdot.js @@ -8,7 +8,7 @@ } }(this, function(jQuery) { /* - * jQuery dotdotdot 3.1.0 + * jQuery dotdotdot 3.2.0 * @requires jQuery 1.7.0 or later * * dotdotdot.frebsite.nl @@ -19,6 +19,6 @@ * License: CC-BY-NC-4.0 * http://creativecommons.org/licenses/by-nc/4.0/ */ -!function(t){"use strict";function e(){h=t(window),s={},r={},o={},t.each([s,r,o],function(t,e){e.add=function(t){t=t.split(" ");for(var n=0,i=t.length;ni)){t[n]=function(t,e){this.$dot=t,this.api=["getInstance","truncate","restore","destroy","watch","unwatch"],this.opts=e;var i=this.$dot.data(n);return i&&i.destroy(),this.init(),this.truncate(),this.opts.watch&&this.watch(),this},t[n].version=i,t[n].uniqueId=0,t[n].defaults={ellipsis:"… ",callback:function(t){},truncate:"word",tolerance:0,keep:null,watch:"window",height:null},t[n].prototype={init:function(){this.watchTimeout=null,this.watchInterval=null,this.uniqueId=t[n].uniqueId++,this.originalContent=this.$dot.contents(),this.originalStyle=this.$dot.attr("style")||"","break-word"!==this.$dot.css("word-wrap")&&this.$dot.css("word-wrap","break-word"),"nowrap"===this.$dot.css("white-space")&&this.$dot.css("white-space","normal"),null===this.opts.height&&(this.opts.height=this._getMaxHeight())},getInstance:function(){return this},truncate:function(){var e=this;this.$inner=this.$dot.wrapInner("
").children().css({display:"block",height:"auto",width:"auto",border:"none",padding:0,margin:0}),this.$inner.contents().detach().end().append(this.originalContent.clone(!0)),this.$inner.find("script, style").addClass(s.keep),this.opts.keep&&this.$inner.find(this.opts.keep).addClass(s.keep),this.$inner.find("*").not("."+s.keep).add(this.$inner).contents().each(function(){var n=this,i=t(this);if(3==n.nodeType){if(i.parent().is("table, thead, tfoot, tr, dl, ul, ol, video"))return void i.remove();if(i.parent().contents().length>1){var r=t(''+e.__getTextContent(n)+"").css({display:"inline",height:"auto",width:"auto",border:"none",padding:0,margin:0});i.replaceWith(r)}}else 8==n.nodeType&&i.remove()}),this.maxHeight=this._getMaxHeight();var n=this._truncateNode(this.$dot);return this.$dot[n?"addClass":"removeClass"](s.truncated),this.$inner.find("."+s.text).each(function(){t(this).replaceWith(t(this).contents())}),this.$inner.find("."+s.keep).removeClass(s.keep),this.$inner.replaceWith(this.$inner.contents()),this.$inner=null,this.opts.callback.call(this.$dot[0],n),n},restore:function(){this.unwatch(),this.$dot.contents().detach().end().append(this.originalContent).attr("style",this.originalStyle).removeClass(s.truncated)},destroy:function(){this.restore(),this.$dot.data(n,null)},watch:function(){var t=this;this.unwatch();var e={};"window"==this.opts.watch?h.on(o.resize+t.uniqueId,function(n){t.watchTimeout&&clearTimeout(t.watchTimeout),t.watchTimeout=setTimeout(function(){e=t._watchSizes(e,h,"width","height")},100)}):this.watchInterval=setInterval(function(){e=t._watchSizes(e,t.$dot,"innerWidth","innerHeight")},500)},unwatch:function(){h.off(o.resize+this.uniqueId),this.watchInterval&&clearInterval(this.watchInterval),this.watchTimeout&&clearTimeout(this.watchTimeout)},_api:function(){var e=this,n={};return t.each(this.api,function(t){var i=this;n[i]=function(){var t=e[i].apply(e,arguments);return"undefined"==typeof t?n:t}}),n},_truncateNode:function(e){var n=this,i=!1,r=!1;return t(e.children().get().reverse()).not("."+s.keep).each(function(){var e=(t(this).contents()[0],t(this));if(!i&&!e.hasClass(s.keep)){if(e.children().length)i=n._truncateNode(e);else if(!n._fits()||r){var o=t("").css("display","none");if(e.replaceWith(o),e.detach(),n._fits()){if("node"==n.opts.truncate)return!0;o.replaceWith(e),i=n._truncateWord(e),i||(r=!0,e.detach())}else o.remove()}e.contents().length||e.remove()}}),i},_truncateWord:function(t){var e=t.contents()[0];if(!e)return!1;for(var n=this,i=this.__getTextContent(e),s=i.indexOf(" ")!==-1?" ":" ",r=i.split(s),o="",h=r.length;h>=0;h--){if(o=r.slice(0,h).join(s),0==h)return"letter"==n.opts.truncate&&(n.__setTextContent(e,r.slice(0,h+1).join(s)),n._truncateLetter(e));if(o.length&&(n.__setTextContent(e,n._addEllipsis(o)),n._fits()))return"letter"!=n.opts.truncate||(n.__setTextContent(e,r.slice(0,h+1).join(s)),n._truncateLetter(e))}return!1},_truncateLetter:function(t){for(var e=this,n=this.__getTextContent(t),i=n.split(""),s="",r=i.length;r>=0;r--)if(s=i.slice(0,r).join(""),s.length&&(e.__setTextContent(t,e._addEllipsis(s)),e._fits()))return!0;return!1},_fits:function(){return this.$inner.innerHeight()<=this.maxHeight+this.opts.tolerance},_addEllipsis:function(e){for(var n=[" "," ",",",";",".","!","?"];t.inArray(e.slice(-1),n)>-1;)e=e.slice(0,-1);return e+=this.opts.ellipsis},_getMaxHeight:function(){if("number"==typeof this.opts.height)return this.opts.height;for(var t=["maxHeight","height"],e=0,n=0;nn)){t[i]=function(t,e){this.$dot=t,this.api=["getInstance","truncate","restore","destroy","watch","unwatch"],this.opts=e;var n=this.$dot.data(i);return n&&n.destroy(),this.init(),this.truncate(),this.opts.watch&&this.watch(),this},t[i].version=n,t[i].uniqueId=0,t[i].defaults={ellipsis:"… ",callback:function(t){},truncate:"word",tolerance:0,keep:null,watch:"window",height:null},t[i].prototype={init:function(){this.watchTimeout=null,this.watchInterval=null,this.uniqueId=t[i].uniqueId++,this.originalContent=this.$dot.contents(),this.originalStyle=this.$dot.attr("style")||"","break-word"!==this.$dot.css("word-wrap")&&this.$dot.css("word-wrap","break-word"),"nowrap"===this.$dot.css("white-space")&&this.$dot.css("white-space","normal"),null===this.opts.height&&(this.opts.height=this._getMaxHeight()),"string"==typeof this.opts.ellipsis&&(this.opts.ellipsis=document.createTextNode(this.opts.ellipsis))},getInstance:function(){return this},truncate:function(){var e=this;this.$inner=this.$dot.wrapInner("
").children().css({display:"block",height:"auto",width:"auto",border:"none",padding:0,margin:0}),this.$inner.empty().append(this.originalContent.clone(!0)),this.$inner.find("script, style").addClass(s.keep),this.opts.keep&&this.$inner.find(this.opts.keep).addClass(s.keep),this.$inner.find("*").not("."+s.keep).add(this.$inner).contents().each(function(){var i=this,n=t(this);if(3==i.nodeType){if(""==t.trim(e.__getTextContent(i))){if(n.parent().is("table, thead, tbody, tfoot, tr, dl, ul, ol, video"))return void n.remove();if(n.prev().is("div, p, table, td, td, dt, dd, li"))return void n.remove();if(n.next().is("div, p, table, td, td, dt, dd, li"))return void n.remove();if(!n.prev().length)return void n.remove();if(!n.next().length)return void n.remove()}}else 8==i.nodeType&&n.remove()}),this.maxHeight=this._getMaxHeight();var i=!this._fits()&&this._truncateNode(this.$dot[0]);return this.$dot[i?"addClass":"removeClass"](s.truncated),this.$inner.find("."+s.keep).removeClass(s.keep),this.$inner.replaceWith(this.$inner.contents()),this.$inner=null,this.opts.callback.call(this.$dot[0],i),i},restore:function(){this.unwatch(),this.$dot.empty().append(this.originalContent).attr("style",this.originalStyle).removeClass(s.truncated)},destroy:function(){this.restore(),this.$dot.data(i,null)},watch:function(){var t=this;this.unwatch();var e={};"window"==this.opts.watch?h.on(o.resize+t.uniqueId,function(i){t.watchTimeout&&clearTimeout(t.watchTimeout),t.watchTimeout=setTimeout(function(){e=t._watchSizes(e,h,"width","height")},100)}):this.watchInterval=setInterval(function(){e=t._watchSizes(e,t.$dot,"innerWidth","innerHeight")},500)},unwatch:function(){h.off(o.resize+this.uniqueId),this.watchInterval&&clearInterval(this.watchInterval),this.watchTimeout&&clearTimeout(this.watchTimeout)},_api:function(){var e=this,i={};return t.each(this.api,function(t){var n=this;i[n]=function(){var t=e[n].apply(e,arguments);return"undefined"==typeof t?i:t}}),i},_truncateNode:function(e){var i=[],n=[];t(e).contents().each(function(){var e=t(this);if(!e.hasClass(s.keep)){var r=document.createComment("");e.replaceWith(r),n.push(this),i.push(r)}});for(var r=0;r");d.append(this.opts.ellipsis),t(a).replaceWith(d),this._fits()?d.replaceWith(a):(d.remove(),a=n[r-1])}return 1==a.nodeType?this._truncateNode(a):this._truncateWord(a)},_truncateWord:function(t){for(var e=t,i=this,n=this.__getTextContent(e),s=n.indexOf(" ")!==-1?" ":" ",r=n.split(s),o="",h=r.length;h>=0;h--)if(o=r.slice(0,h).join(s),i.__setTextContent(e,i._addEllipsis(o)),i._fits())return"letter"!=i.opts.truncate||(i.__setTextContent(e,r.slice(0,h+1).join(s)),i._truncateLetter(e));return!1},_truncateLetter:function(t){for(var e=this,i=this.__getTextContent(t),n=i.split(""),s="",r=n.length;r>=0;r--)if(s=n.slice(0,r).join(""),s.length&&(e.__setTextContent(t,e._addEllipsis(s)),e._fits()))return!0;return!1},_fits:function(){return this.$inner.innerHeight()<=this.maxHeight+this.opts.tolerance},_addEllipsis:function(e){for(var i=[" "," ",",",";",".","!","?"];t.inArray(e.slice(-1),i)>-1;)e=e.slice(0,-1);return e+=this.__getTextContent(this.opts.ellipsis)},_getMaxHeight:function(){if("number"==typeof this.opts.height)return this.opts.height;for(var t=["maxHeight","height"],e=0,i=0;i", "license" : "CC-BY-NC-4.0", diff --git a/src/jquery.dotdotdot.js b/src/jquery.dotdotdot.js index 89d8a07..6fab568 100644 --- a/src/jquery.dotdotdot.js +++ b/src/jquery.dotdotdot.js @@ -1,5 +1,5 @@ /* - * jQuery dotdotdot 3.1.0 + * jQuery dotdotdot 3.2.0 * @requires jQuery 1.7.0 or later * * dotdotdot.frebsite.nl @@ -15,7 +15,7 @@ 'use strict'; var _PLUGIN_ = 'dotdotdot'; - var _VERSION_ = '3.1.0'; + var _VERSION_ = '3.2.0'; if ( $[ _PLUGIN_ ] && $[ _PLUGIN_ ].version > _VERSION_ ) { @@ -87,6 +87,12 @@ { this.opts.height = this._getMaxHeight(); } + + if ( typeof this.opts.ellipsis == 'string' ) + { + this.opts.ellipsis = document.createTextNode( this.opts.ellipsis ); + } + }, getInstance: function() @@ -115,9 +121,7 @@ // Set original content this.$inner - .contents() - .detach() - .end() + .empty() .append( this.originalContent.clone( true ) ); @@ -152,26 +156,33 @@ { // Remove whitespace where it does not take up space in the DOM - if ( $e.parent().is( 'table, thead, tfoot, tr, dl, ul, ol, video' ) ) + if ( $.trim( that.__getTextContent( e ) ) == '' ) { - $e.remove(); - return; - } - - // Wrap text in a node (during truncation) - if ( $e.parent().contents().length > 1 ) - { - var $d = $( '' + that.__getTextContent( e ) + '' ) - .css({ - 'display' : 'inline', - 'height' : 'auto', - 'width' : 'auto', - 'border' : 'none', - 'padding' : 0, - 'margin' : 0 - }); - - $e.replaceWith( $d ); + if ( $e.parent().is( 'table, thead, tbody, tfoot, tr, dl, ul, ol, video' ) ) + { + $e.remove(); + return; + } + if ( $e.prev().is( 'div, p, table, td, td, dt, dd, li' ) ) + { + $e.remove(); + return; + } + if ( $e.next().is( 'div, p, table, td, td, dt, dd, li' ) ) + { + $e.remove(); + return; + } + if ( !$e.prev().length ) + { + $e.remove(); + return; + } + if ( !$e.next().length ) + { + $e.remove(); + return; + } } } @@ -189,19 +200,12 @@ // Truncate the text - var isTruncated = this._truncateNode( this.$dot ); - this.$dot[ isTruncated ? 'addClass' : 'removeClass' ]( _c.truncated ); + var isTruncated = this._fits() + ? false + : this._truncateNode( this.$dot[ 0 ] ); - - // Unwrap text from the temporarely node - this.$inner - .find( '.' + _c.text ) - .each( - function() - { - $(this).replaceWith( $(this).contents() ); - } - ); + + this.$dot[ isTruncated ? 'addClass' : 'removeClass' ]( _c.truncated ); // Remove "keep" class @@ -209,7 +213,6 @@ .find( '.' + _c.keep ) .removeClass( _c.keep ); - // Remove inner node this.$inner.replaceWith( this.$inner.contents() ); this.$inner = null; @@ -223,9 +226,7 @@ this.unwatch(); this.$dot - .contents() - .detach() - .end() + .empty() .append( this.originalContent ) .attr( 'style', this.originalStyle ) .removeClass( _c.truncated ); @@ -312,88 +313,100 @@ return api; }, - _truncateNode: function( $elem ) + _truncateNode: function( _elem ) { + var that = this; - var isTruncated = false; - var forceEllipsis = false; - $($elem - .children() - .get() - .reverse() - ) - .not( '.' + _c.keep ) + var _coms = [], + _elms = []; + + // Empty the node + // -> replace all contents with comments + $(_elem) + .contents() .each( function() { - var e = $(this).contents()[ 0 ], - $e = $(this); - - if ( isTruncated ) + var $e = $(this); + if ( !$e.hasClass( _c.keep ) ) { - return; - } - if ( $e.hasClass( _c.keep) ) - { - return; - } - - if ( $e.children().length ) - { - isTruncated = that._truncateNode( $e ); - } - else - { - if ( !that._fits() || forceEllipsis ) - { - var $x = $('').css( 'display', 'none' ); - $e.replaceWith( $x ); - $e.detach(); - - if ( that._fits() ) - { - if ( that.opts.truncate == 'node' ) - { - return true; - } - - $x.replaceWith( $e ); - isTruncated = that._truncateWord( $e ); - - if ( !isTruncated ) - { - forceEllipsis = true; - $e.detach(); - } - } - else - { - $x.remove(); - } - } - } + var c = document.createComment( '' ); + $e.replaceWith( c ); - // Remove empty nodes - if ( !$e.contents().length ) - { - $e.remove(); + _elms.push( this ); + _coms.push( c ); } } ); - return isTruncated; - }, + // Re-fill the node + // -> replace comments with contents until it doesn't fit anymore + for ( var e = 0; e < _elms.length; e++ ) + { + $(_coms[ e ]).replaceWith( _elms[ e ] ); - _truncateWord: function( $e ) - { - var e = $e.contents()[ 0 ]; + $(_elms[ e ]).append( this.opts.ellipsis ); + var fits = this._fits(); + $(this.opts.ellipsis, _elms[ e ]).remove(); - if ( !e ) + if ( !fits ) + { + break; + } + } + + // Remove left over comments + for ( var c = e; c < _coms.length; c++ ) + { + $(_coms[ c ]).remove(); + } + + // Get last node + // -> the node that overflows + var _last = _elms[ Math.min( e, _elms.length - 1 ) ]; + + // Border case + // -> the last node with only an ellipsis in it... + if ( _last.nodeType == 1 ) { - return false; + var $e = $('<' + _last.nodeName + ' />'); + $e.append( this.opts.ellipsis ); + + $(_last).replaceWith( $e ); + + // ... fits + // -> Restore the full last node + if ( this._fits() ) + { + $e.replaceWith( _last ); + } + + // ... doesn't fit + // -> remove it and go back one node + else + { + $e.remove(); + _last = _elms[ e - 1 ]; + } } + + if ( _last.nodeType == 1 ) + { + return this._truncateNode( _last ); + } + else + { + return this._truncateWord( _last ); + } + }, + + _truncateWord: function( _elem ) + { + + var e = _elem; + var that = this; var txt = this.__getTextContent( e ), @@ -405,22 +418,6 @@ { str = arr.slice( 0, a ).join( sep ); - // If even the first child didn't make it - if ( a == 0 ) - { - if ( that.opts.truncate == 'letter' ) - { - that.__setTextContent( e, arr.slice( 0, a + 1 ).join( sep ) ); - return that._truncateLetter( e ); - } - return false; - } - - if ( !str.length ) - { - continue; - } - that.__setTextContent( e, that._addEllipsis( str ) ); if ( that._fits() ) @@ -477,7 +474,7 @@ { txt = txt.slice( 0, -1 ); } - txt += this.opts.ellipsis; + txt += this.__getTextContent( this.opts.ellipsis ); return txt; }, @@ -622,11 +619,10 @@ // Classnames _c.ddd = function( c ) { return 'ddd-' + c; }; - _c.add( 'truncated keep text' ); + _c.add( 'truncated keep' ); // Datanames _d.ddd = function( d ) { return 'ddd-' + d; }; - _d.add( 'text' ); // Eventnames _e.ddd = function( e ) { return e + '.ddd'; };