PageSourceSearch

https://ayurvedajournals.com/assets/menu/jquery.dotdotdot.js

js ayurvedajournals.com collected 2026-10-03 00:39:37 UTC 12,434 bytes, 653 lines download raw bytes

1/*
2 *	jQuery dotdotdot 3.2.2
3 *	@requires jQuery 1.7.0 or later
4 *
5 *	dotdotdot.frebsite.nl
6 *
7 *	Copyright (c) Fred Heusschen
8 *	www.frebsite.nl
9 *
10 *	License: CC-BY-NC-4.0
11 *	http://creativecommons.org/licenses/by-nc/4.0/
12 */
13
14(function( $ ) {
15	'use strict';
16	
17	var _PLUGIN_    = 'dotdotdot';
18	var _VERSION_   = '3.2.2';
19
20	if ( $[ _PLUGIN_ ] && $[ _PLUGIN_ ].version > _VERSION_ )
21	{
22		return;
23	}
24
25
26
27	/*
28		The class
29	*/
30	$[ _PLUGIN_ ] = function( $container, opts )
31	{
32		this.$dot 	= $container;
33		this.api	= [ 'getInstance', 'truncate', 'restore', 'destroy', 'watch', 'unwatch' ];
34		this.opts	= opts;
35
36		var oldAPI = this.$dot.data( _PLUGIN_ );
37		if ( oldAPI )
38		{
39			oldAPI.destroy();
40		}
41
42		this.init();
43		this.truncate();
44
45		if ( this.opts.watch )
46		{
47			this.watch();
48		}
49
50		return this;
51	};
52
53	$[ _PLUGIN_ ].version 	= _VERSION_;
54	$[ _PLUGIN_ ].uniqueId 	= 0;
55
56	$[ _PLUGIN_ ].defaults  = {
57		ellipsis		: '\u2026 ',
58		callback		: function( isTruncated ) {},
59		truncate 		: 'word',
60		tolerance		: 0,
61		keep			: null,
62		watch			: 'window',
63		height 			: null
64	};
65
66
67	$[ _PLUGIN_ ].prototype = {
68
69		init: function()
70		{
71			this.watchTimeout		= null;
72			this.watchInterval		= null;
73			this.uniqueId 			= $[ _PLUGIN_ ].uniqueId++;
74			this.originalStyle		= this.$dot.attr( 'style' ) || '';
75			this.originalContent 	= this._getOriginalContent();
76			
77			if ( this.$dot.css( 'word-wrap' ) !== 'break-word' )
78			{
79				this.$dot.css( 'word-wrap', 'break-word' );
80			}
81			if ( this.$dot.css( 'white-space' ) === 'nowrap' )
82			{
83				this.$dot.css( 'white-space', 'normal' );
84			}
85
86			if ( this.opts.height === null )
87			{
88				this.opts.height = this._getMaxHeight();
89			}
90
91			if ( typeof this.opts.ellipsis == 'string' )
92			{
93				this.opts.ellipsis = document.createTextNode( this.opts.ellipsis );
94			}
95		},
96
97		getInstance: function()
98		{
99			return this;
100		},
101
102		truncate: function()
103		{
104			var that = this;
105
106
107			//	Add inner node for measuring the height
108			this.$inner = this.$dot
109				.wrapInner( '<div />' )
110				.children()
111				.css({
112					'display'	: 'block',
113					'height'	: 'auto',
114					'width'		: 'auto',
115					'border'	: 'none',
116					'padding'	: 0,
117					'margin'	: 0
118				});
119
120
121			//	Set original content
122			this.$inner
123				.empty()
124				.append( this.originalContent.clone( true ) );
125
126
127			//	Get max height
128			this.maxHeight = this._getMaxHeight();
129
130
131			//	Truncate the text
132			var isTruncated = false;
133			if ( !this._fits() )
134			{
135				isTruncated = true;
136				this._truncateToNode( this.$inner[ 0 ] );
137			}
138
139			this.$dot[ isTruncated ? 'addClass' : 'removeClass' ]( _c.truncated );
140
141
142			//	Remove inner node
143			this.$inner.replaceWith( this.$inner.contents() );
144			this.$inner = null;
145
146
147			//	 Callback
148			this.opts.callback.call( this.$dot[ 0 ], isTruncated );
149
150			return isTruncated;
151		},
152
153		restore: function()
154		{
155			this.unwatch();
156
157			this.$dot
158				.empty()
159				.append( this.originalContent )
160				.attr( 'style', this.originalStyle )
161				.removeClass( _c.truncated );
162		},
163
164		destroy: function()
165		{
166			this.restore();
167			this.$dot.data( _PLUGIN_, null );
168		},
169
170		watch: function()
171		{
172			var that = this;
173
174			this.unwatch();
175
176			var oldSizes = {};
177
178			if ( this.opts.watch == 'window' )
179			{
180				$wndw.on(
181					_e.resize + that.uniqueId,
182					function( e )
183					{
184						if ( that.watchTimeout )
185						{
186							clearTimeout( that.watchTimeout );
187						}
188						that.watchTimeout = setTimeout(
189							function() {
190
191								oldSizes = that._watchSizes( oldSizes, $wndw, 'width', 'height' );
192
193							}, 100
194						);
195					}
196				);
197
198			}
199			else
200			{
201				this.watchInterval = setInterval(
202					function()
203					{
204						oldSizes = that._watchSizes( oldSizes, that.$dot, 'innerWidth', 'innerHeight' );
205
206					}, 500
207				);
208			}
209		},
210
211		unwatch: function()
212		{
213			$wndw.off( _e.resize + this.uniqueId );
214
215			if ( this.watchInterval )
216			{
217				clearInterval( this.watchInterval );
218			}
219
220			if ( this.watchTimeout )
221			{
222				clearTimeout( this.watchTimeout );
223			}
224		},
225
226		_api: function()
227		{
228			var that = this,
229				api = {};
230
231			$.each( this.api,
232				function( i )
233				{
234					var fn = this;
235					api[ fn ] = function()
236					{
237						var re = that[ fn ].apply( that, arguments );
238						return ( typeof re == 'undefined' ) ? api : re;
239					};
240				}
241			);
242			return api;
243		},
244
245		_truncateToNode: function( _elem )
246		{
247
248			var that = this;
249
250			var _coms = [],
251				_elms = [];
252
253			//	Empty the node 
254			//		-> replace all contents with comments
255			$(_elem)
256				.contents()
257				.each(
258					function()
259					{
260						var $e = $(this);
261						if ( !$e.hasClass( _c.keep ) )
262						{
263							var c = document.createComment( '' );
264							$e.replaceWith( c );
265
266							_elms.push( this );
267							_coms.push( c );
268						}
269					}
270				);
271
272			if ( !_elms.length )
273			{
274				return;
275			}
276
277			//	Re-fill the node 
278			//		-> replace comments with contents until it doesn't fit anymore
279			for ( var e = 0; e < _elms.length; e++ )
280			{
281
282				$(_coms[ e ]).replaceWith( _elms[ e ] );
283
284				$(_elms[ e ]).append( this.opts.ellipsis );
285				var fits = this._fits();
286				$(this.opts.ellipsis, _elms[ e ]).remove();
287
288				if ( !fits )
289				{
290					if ( this.opts.truncate == 'node' && e > 1 )
291					{
292						$(_elms[ e - 2 ]).remove();
293						return;
294					}
295					break;
296				}
297			}
298
299			//	Remove left over comments
300			for ( var c = e; c < _coms.length; c++ )
301			{
302				$(_coms[ c ]).remove();
303			}
304
305			//	Get last node 
306			//		-> the node that overflows
307
308			var _last = _elms[ Math.max( 0, Math.min( e, _elms.length - 1 ) ) ];
309
310			//	Border case
311			//		-> the last node with only an ellipsis in it...
312			if ( _last.nodeType == 1 )
313			{
314
315				var $e = $('<' + _last.nodeName + ' />');
316				$e.append( this.opts.ellipsis );
317
318				$(_last).replaceWith( $e );
319
320				//	... fits
321				//		-> Restore the full last node
322				if ( this._fits() )
323				{
324					$e.replaceWith( _last );
325				}
326
327				//	... doesn't fit
328				//		-> remove it and go back one node
329				else
330				{
331					$e.remove();
332					_last = _elms[ Math.max( 0, e - 1 ) ];
333				}
334			}
335
336			//	Proceed inside last node
337			if ( _last.nodeType == 1 )
338			{
339				this._truncateToNode( _last );
340			}
341			else
342			{
343				this._truncateToWord( _last );
344			}
345		},
346
347		_truncateToWord: function( _elem )
348		{
349
350			var e = _elem;
351
352			var that = this;
353
354			var txt = this.__getTextContent( e ),
355				sep = ( txt.indexOf( ' ' ) !== -1 ) ? ' ' : '\u3000',
356				arr = txt.split( sep ),
357				str = '';
358
359			for ( var a = arr.length; a >= 0; a-- )
360			{
361				str = arr.slice( 0, a ).join( sep );
362
363				that.__setTextContent( e, that._addEllipsis( str ) );
364
365				if ( that._fits() )
366				{
367					if ( that.opts.truncate == 'letter' )
368					{
369						that.__setTextContent( e, arr.slice( 0, a + 1 ).join( sep ) );
370						that._truncateToLetter( e );
371					}
372					break;
373				}
374			}
375		},
376
377		_truncateToLetter: function( e )
378		{
379			var that = this;
380
381			var txt = this.__getTextContent( e ),
382				arr = txt.split( '' ),
383				str = '';
384
385			for ( var a = arr.length; a >= 0; a-- )
386			{
387				str = arr.slice( 0, a ).join( '' );
388
389				if ( !str.length )
390				{
391					continue;
392				}
393
394				that.__setTextContent( e, that._addEllipsis( str ) );
395
396				if ( that._fits() )
397				{
398					break;
399				}
400			}
401		},
402
403		_fits: function()
404		{
405			return ( this.$inner.innerHeight() <= this.maxHeight + this.opts.tolerance );
406		},
407
408		_addEllipsis: function( txt )
409		{
410			var remove = [' ', '\u3000', ',', ';', '.', '!', '?'];
411
412			while ( $.inArray( txt.slice( -1 ), remove ) > -1 )
413			{
414				txt = txt.slice( 0, -1 );
415			}
416			txt += this.__getTextContent( this.opts.ellipsis );
417
418			return txt;
419		},
420
421		_getOriginalContent: function()
422		{
423			var that = this;
424
425			//	Add "keep" class to nodes to keep
426			this.$dot
427				.find( 'script, style' )
428				.addClass( _c.keep );
429
430			if ( this.opts.keep )
431			{
432				this.$dot
433					.find( this.opts.keep )
434					.addClass( _c.keep );
435			}
436
437			//	Filter out unneeded whitespace
438			this.$dot
439				.find( '*' )
440				.not( '.' + _c.keep )
441				.add( this.$dot )
442				.contents()
443				.each(
444					function()
445					{
446
447						var e = this,
448							$e = $(this);
449
450						//	Text nodes
451						if ( e.nodeType == 3 )
452						{
453
454							//	Remove whitespace where it does not take up space in the DOM
455							if ( $.trim( that.__getTextContent( e ) ) == '' )
456							{
457								if ( $e.parent().is( 'table, thead, tbody, tfoot, tr, dl, ul, ol, video' ) )
458								{
459									$e.remove();
460									return;
461								}
462								if ( $e.prev().is( 'div, p, table, td, td, dt, dd, li' ) )
463								{
464									$e.remove();
465									return;
466								}
467								if ( $e.next().is( 'div, p, table, td, td, dt, dd, li' ) )
468								{
469									$e.remove();
470									return;
471								}
472								if ( !$e.prev().length )
473								{
474									$e.remove();
475									return;
476								}
477								if ( !$e.next().length )
478								{
479									$e.remove();
480									return;
481								}
482							}
483						}
484
485						//	Comment nodes
486						else if ( e.nodeType == 8 )
487						{
488							$e.remove();
489						}
490
491					}
492				);
493
494			return this.$dot.contents();
495		},
496
497		_getMaxHeight: function()
498		{
499			if ( typeof this.opts.height == 'number' )
500			{
501				return this.opts.height;
502			}
503
504			//	Find smallest CSS height
505			var arr = [ 'maxHeight', 'height' ],
506				hgh = 0;
507 
508			for ( var a = 0; a < arr.length; a++ )
509			{
510				hgh = window.getComputedStyle( this.$dot[ 0 ] )[ arr[ a ] ];
511				if ( hgh.slice( -2 ) == 'px' )
512				{
513					hgh = parseFloat( hgh );
514					break;
515				}
516			}
517
518			//	Remove padding-top/bottom when needed.
519			var arr = [];
520			switch ( this.$dot.css( 'boxSizing' ) )
521			{
522				case 'border-box':
523					arr.push( 'borderTopWidth' );
524					arr.push( 'borderBottomWidth' );
525					//	no break -> padding needs to be added too
526
527				case 'padding-box':
528					arr.push( 'paddingTop' );
529					arr.push( 'paddingBottom' );
530					break;
531			}
532			for ( var a = 0; a < arr.length; a++ )
533			{
534				var p = window.getComputedStyle( this.$dot[ 0 ] )[ arr[ a ] ];
535				if ( p.slice( -2 ) == 'px' )
536				{
537					hgh -= parseFloat( p );
538				}
539			}
540
541			//	Sanitize
542			return Math.max( hgh, 0 );
543		},
544
545		_watchSizes: function( oldSizes, $elem, width, height )
546		{
547			if ( this.$dot.is( ':visible' ) )
548			{
549				var newSizes = {
550					'width'		: $elem[ width  ](),
551					'height'	: $elem[ height ]()
552				};
553
554				if ( oldSizes.width != newSizes.width || oldSizes.height != newSizes.height )
555				{
556					this.truncate();
557				}
558
559				return newSizes;
560			}
561			return oldSizes;
562		},
563
564		__getTextContent: function( elem )
565		{
566			var arr = [ 'nodeValue', 'textContent', 'innerText' ];
567			for ( var a = 0; a < arr.length; a++ )
568			{
569				if ( typeof elem[ arr[ a ] ] == 'string' )
570				{
571					return elem[ arr[ a ] ];
572				}
573			}
574			return '';
575		},
576		__setTextContent: function( elem, content )
577		{
578			var arr = [ 'nodeValue', 'textContent', 'innerText' ];
579			for ( var a = 0; a < arr.length; a++ )
580			{
581				elem[ arr[ a ] ] = content;
582			}
583		}
584	};
585
586
587
588	/*
589		The jQuery plugin
590	*/
591	$.fn[ _PLUGIN_ ] = function( opts )
592	{
593		initPlugin();
594
595		opts = $.extend( true, {}, $[ _PLUGIN_ ].defaults, opts );
596
597		return this.each(
598			function()
599			{
600				$(this).data( _PLUGIN_, new $[ _PLUGIN_ ]( $(this), opts )._api() );
601			}
602		);
603	};
604
605
606
607	/*
608		Global variables
609	*/
610	var _c, _d, _e, $wndw;
611
612	function initPlugin()
613	{
614		$wndw = $(window);
615
616		//	Classnames, Datanames, Eventnames
617		_c = {};
618		_d = {};
619		_e = {};
620
621		$.each( [ _c, _d, _e ],
622			function( i, o )
623			{
624				o.add = function( a )
625				{
626					a = a.split( ' ' );
627					for ( var b = 0, l = a.length; b < l; b++ )
628					{
629						o[ a[ b ] ] = o.ddd( a[ b ] );
630					}
631				};
632			}
633		);
634
635		//	Classnames
636		_c.ddd = function( c ) { return 'ddd-' + c; };
637		_c.add( 'truncated keep' );
638
639		//	Datanames
640		_d.ddd = function( d ) { return 'ddd-' + d; };
641
642		//	Eventnames
643		_e.ddd = function( e ) { return e + '.ddd'; };
644		_e.add( 'resize' );
645
646
647		//	Only once
648		initPlugin = function() {};
649
650	}
651
652
653})( jQuery );

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.