1/* 2 * jQuery dotdotdot 1.7.4 3 * 4 * Copyright (c) Fred Heusschen 5 * www.frebsite.nl 6 * 7 * Plugin website: 8 * dotdotdot.frebsite.nl 9 * 10 * Licensed under the MIT license. 11 * http://en.wikipedia.org/wiki/MIT_License 12 */ 13 14(function( $, undef ) 15{ 16 if ( $.fn.dotdotdot ) 17 { 18 return; 19 } 20 21 $.fn.dotdotdot = function( o ) 22 { 23 if ( this.length == 0 ) 24 { 25 $.fn.dotdotdot.debug( 'No element found for "' + this.selector + '".' ); 26 return this; 27 } 28 if ( this.length > 1 ) 29 { 30 return this.each( 31 function() 32 { 33 $(this).dotdotdot( o ); 34 } 35 ); 36 } 37 38 39 var $dot = this; 40 41 if ( $dot.data( 'dotdotdot' ) ) 42 { 43 $dot.trigger( 'destroy.dot' ); 44 } 45 46 $dot.data( 'dotdotdot-style', $dot.attr( 'style' ) || '' ); 47 $dot.css( 'word-wrap', 'break-word' ); 48 if ($dot.css( 'white-space' ) === 'nowrap') 49 { 50 $dot.css( 'white-space', 'normal' ); 51 } 52 53 $dot.bind_events = function() 54 { 55 $dot.bind( 56 'update.dot', 57 function( e, c ) 58 { 59 $dot.removeClass("is-truncated"); 60 e.preventDefault(); 61 e.stopPropagation(); 62 63 switch( typeof opts.height ) 64 { 65 case 'number': 66 opts.maxHeight = opts.height; 67 break; 68 69 case 'function': 70 opts.maxHeight = opts.height.call( $dot[ 0 ] ); 71 break; 72 73 default: 74 opts.maxHeight = getTrueInnerHeight( $dot ); 75 break; 76 } 77 78 opts.maxHeight += opts.tolerance; 79 80 if ( typeof c != 'undefined' ) 81 { 82 if ( typeof c == 'string' || ('nodeType' in c && c.nodeType === 1) ) 83 { 84 c = $('<div />').append( c ).contents(); 85 } 86 if ( c instanceof $ ) 87 { 88 orgContent = c; 89 } 90 } 91 92 $inr = $dot.wrapInner( '<div class="dotdotdot" />' ).children(); 93 $inr.contents() 94 .detach() 95 .end() 96 .append( orgContent.clone( true ) ) 97 .find( 'br' ) 98 .replaceWith( ' <br /> ' ) 99 .end() 100 .css({ 101 'height' : 'auto', 102 'width' : 'auto', 103 'border' : 'none', 104 'padding' : 0, 105 'margin' : 0 106 }); 107 108 var after = false, 109 trunc = false; 110 111 if ( conf.afterElement ) 112 { 113 after = conf.afterElement.clone( true ); 114 after.show(); 115 conf.afterElement.detach(); 116 } 117 118 if ( test( $inr, opts ) ) 119 { 120 if ( opts.wrap == 'children' ) 121 { 122 trunc = children( $inr, opts, after ); 123 } 124 else 125 { 126 trunc = ellipsis( $inr, $dot, $inr, opts, after ); 127 } 128 } 129 $inr.replaceWith( $inr.contents() ); 130 $inr = null; 131 132 if ( $.isFunction( opts.callback ) ) 133 { 134 opts.callback.call( $dot[ 0 ], trunc, orgContent ); 135 } 136 137 conf.isTruncated = trunc; 138 return trunc; 139 } 140 141 ).bind( 142 'isTruncated.dot', 143 function( e, fn ) 144 { 145 e.preventDefault(); 146 e.stopPropagation(); 147 148 if ( typeof fn == 'function' ) 149 { 150 fn.call( $dot[ 0 ], conf.isTruncated ); 151 } 152 return conf.isTruncated; 153 } 154 155 ).bind( 156 'originalContent.dot', 157 function( e, fn ) 158 { 159 e.preventDefault(); 160 e.stopPropagation(); 161 162 if ( typeof fn == 'function' ) 163 { 164 fn.call( $dot[ 0 ], orgContent ); 165 } 166 return orgContent; 167 } 168 169 ).bind( 170 'destroy.dot', 171 function( e ) 172 { 173 e.preventDefault(); 174 e.stopPropagation(); 175 176 $dot.unwatch() 177 .unbind_events() 178 .contents() 179 .detach() 180 .end() 181 .append( orgContent ) 182 .attr( 'style', $dot.data( 'dotdotdot-style' ) || '' ) 183 .data( 'dotdotdot', false ); 184 } 185 ); 186 return $dot; 187 }; // /bind_events 188 189 $dot.unbind_events = function() 190 { 191 $dot.unbind('.dot'); 192 return $dot; 193 }; // /unbind_events 194 195 $dot.watch = function() 196 { 197 $dot.unwatch(); 198 if ( opts.watch == 'window' ) 199 { 200 var $window = $(window), 201 _wWidth = $window.width(), 202 _wHeight = $window.height(); 203 204 $window.bind( 205 'resize.dot' + conf.dotId, 206 function() 207 { 208 if ( _wWidth != $window.width() || _wHeight != $window.height() || !opts.windowResizeFix ) 209 { 210 _wWidth = $window.width(); 211 _wHeight = $window.height(); 212 213 if ( watchInt ) 214 { 215 clearInterval( watchInt ); 216 } 217 watchInt = setTimeout( 218 function() 219 { 220 $dot.trigger( 'update.dot' ); 221 }, 100 222 ); 223 } 224 } 225 ); 226 } 227 else 228 { 229 watchOrg = getSizes( $dot ); 230 watchInt = setInterval( 231 function() 232 { 233 if ( $dot.is( ':visible' ) ) 234 { 235 var watchNew = getSizes( $dot ); 236 if ( watchOrg.width != watchNew.width || 237 watchOrg.height != watchNew.height ) 238 { 239 $dot.trigger( 'update.dot' );
240 watchOrg = watchNew; 241 } 242 } 243 }, 500 244 ); 245 } 246 return $dot; 247 }; 248 $dot.unwatch = function() 249 { 250 $(window).unbind( 'resize.dot' + conf.dotId ); 251 if ( watchInt ) 252 { 253 clearInterval( watchInt ); 254 } 255 return $dot; 256 }; 257 258 var orgContent = $dot.contents(), 259 opts = $.extend( true, {}, $.fn.dotdotdot.defaults, o ), 260 conf = {}, 261 watchOrg = {}, 262 watchInt = null, 263 $inr = null; 264 265 266 if ( !( opts.lastCharacter.remove instanceof Array ) ) 267 { 268 opts.lastCharacter.remove = $.fn.dotdotdot.defaultArrays.lastCharacter.remove; 269 } 270 if ( !( opts.lastCharacter.noEllipsis instanceof Array ) ) 271 { 272 opts.lastCharacter.noEllipsis = $.fn.dotdotdot.defaultArrays.lastCharacter.noEllipsis; 273 } 274 275 276 conf.afterElement = getElement( opts.after, $dot ); 277 conf.isTruncated = false; 278 conf.dotId = dotId++; 279 280 281 $dot.data( 'dotdotdot', true ) 282 .bind_events() 283 .trigger( 'update.dot' ); 284 285 if ( opts.watch ) 286 { 287 $dot.watch(); 288 } 289 290 return $dot; 291 }; 292 293 294 // public 295 $.fn.dotdotdot.defaults = { 296 'ellipsis' : '... ', 297 'wrap' : 'word', 298 'fallbackToLetter' : true, 299 'lastCharacter' : {}, 300 'tolerance' : 0, 301 'callback' : null, 302 'after' : null, 303 'height' : null, 304 'watch' : false, 305 'windowResizeFix' : true 306 }; 307 $.fn.dotdotdot.defaultArrays = { 308 'lastCharacter' : { 309 'remove' : [ ' ', '\u3000', ',', ';', '.', '!', '?' ], 310 'noEllipsis' : [] 311 } 312 }; 313 $.fn.dotdotdot.debug = function( msg ) {}; 314 315 316 // private 317 var dotId = 1; 318 319 function children( $elem, o, after ) 320 { 321 var $elements = $elem.children(), 322 isTruncated = false; 323 324 $elem.empty(); 325 326 for ( var a = 0, l = $elements.length; a < l; a++ ) 327 { 328 var $e = $elements.eq( a ); 329 $elem.append( $e ); 330 if ( after ) 331 {
332 $elem.append( after ); 333 } 334 if ( test( $elem, o ) ) 335 { 336 $e.remove(); 337 isTruncated = true; 338 break; 339 } 340 else 341 { 342 if ( after ) 343 { 344 after.detach(); 345 } 346 } 347 } 348 return isTruncated; 349 } 350 function ellipsis( $elem, $d, $i, o, after ) 351 { 352 var isTruncated = false; 353 354 // Don't put the ellipsis directly inside these elements 355 var notx = 'a, table, thead, tbody, tfoot, tr, col, colgroup, object, embed, param, ol, ul, dl, blockquote, select, optgroup, option, textarea, script, style'; 356 357 // Don't remove these elements even if they are after the ellipsis 358 var noty = 'script, .dotdotdot-keep'; 359 360 $elem 361 .contents() 362 .detach() 363 .each( 364 function() 365 { 366 367 var e = this, 368 $e = $(e); 369 370 if ( typeof e == 'undefined' ) 371 { 372 return true; 373 } 374 else if ( $e.is( noty ) ) 375 { 376 $elem.append( $e ); 377 } 378 else if ( isTruncated ) 379 { 380 return true; 381 } 382 else 383 { 384 $elem.append( $e ); 385 if ( after && !$e.is( o.after ) && !$e.find( o.after ).length ) 386 { 387 $elem[ $elem.is( notx ) ? 'after' : 'append' ]( after ); 388 } 389 if ( test( $i, o ) ) 390 { 391 if ( e.nodeType == 3 ) // node is TEXT 392 { 393 isTruncated = ellipsisElement( $e, $d, $i, o, after ); 394 } 395 else 396 { 397 isTruncated = ellipsis( $e, $d, $i, o, after ); 398 } 399 400 if ( !isTruncated ) 401 { 402 $e.detach(); 403 isTruncated = true; 404 } 405 } 406 407 if ( !isTruncated ) 408 { 409 if ( after ) 410 { 411 after.detach(); 412 } 413 } 414 } 415 } 416 ); 417 $d.addClass("is-truncated"); 418 return isTruncated; 419 } 420 function ellipsisElement( $e, $d, $i, o, after ) 421 { 422 var e = $e[ 0 ]; 423 424 if ( !e ) 425 { 426 return false; 427 } 428 429 var txt = getTextContent( e ), 430 space = ( txt.indexOf(' ') !== -1 ) ? ' ' : '\u3000', 431 separator = ( o.wrap == 'letter' ) ? '' : space, 432 textArr = txt.split( separator ), 433 position = -1, 434 midPos = -1, 435 startPos = 0, 436 endPos = textArr.length - 1; 437 438 439 // Only one word 440 if ( o.fallbackToLetter && startPos == 0 && endPos == 0 ) 441 { 442 separator = ''; 443 textArr = txt.split( separator ); 444 endPos = textArr.length - 1; 445 } 446 447 while ( startPos <= endPos && !( startPos == 0 && endPos == 0 ) ) 448 { 449 var m = Math.floor( ( startPos + endPos ) / 2 ); 450 if ( m == midPos ) 451 { 452 break; 453 } 454 midPos = m; 455 456 setTextContent( e, textArr.slice( 0, midPos + 1 ).join( separator ) + o.ellipsis ); 457 $i.children() 458 .each( 459 function() 460 { 461 $(this).toggle().toggle(); 462 } 463 ); 464 465 if ( !test( $i, o ) ) 466 { 467 position = midPos; 468 startPos = midPos; 469 } 470 else 471 { 472 endPos = midPos; 473 474 // Fallback to letter 475 if (o.fallbackToLetter && startPos == 0 && endPos == 0 ) 476 { 477 separator = ''; 478 textArr = textArr[ 0 ].split( separator ); 479 position = -1; 480 midPos = -1; 481 startPos = 0; 482 endPos = textArr.length - 1; 483 } 484 } 485 } 486 487 if ( position != -1 && !( textArr.length == 1 && textArr[ 0 ].length == 0 ) ) 488 { 489 txt = addEllipsis( textArr.slice( 0, position + 1 ).join( separator ), o ); 490 setTextContent( e, txt ); 491 } 492 else 493 { 494 var $w = $e.parent(); 495 $e.detach(); 496 497 var afterLength = ( after && after.closest($w).length ) ? after.length : 0; 498 499 if ( $w.contents().length > afterLength ) 500 { 501 e = findLastTextNode( $w.contents().eq( -1 - afterLength ), $d ); 502 } 503 else 504 { 505 e = findLastTextNode( $w, $d, true ); 506 if ( !afterLength ) 507 { 508 $w.detach(); 509 } 510 } 511 if ( e ) 512 { 513 txt = addEllipsis( getTextContent( e ), o ); 514 setTextContent( e, txt ); 515 if ( afterLength && after ) 516 { 517 $(e).parent().append( after ); 518 } 519 } 520 } 521 522 return true; 523 } 524 function test( $i, o ) 525 { 526 return $i.innerHeight() > o.maxHeight; 527 } 528 function addEllipsis( txt, o ) 529 { 530 while( $.inArray( txt.slice( -1 ), o.lastCharacter.remove ) > -1 ) 531 { 532 txt = txt.slice( 0, -1 ); 533 }
534 if ( $.inArray( txt.slice( -1 ), o.lastCharacter.noEllipsis ) < 0 ) 535 { 536 txt += o.ellipsis; 537 } 538 return txt; 539 } 540 function getSizes( $d ) 541 { 542 return { 543 'width' : $d.innerWidth(), 544 'height': $d.innerHeight() 545 }; 546 } 547 function setTextContent( e, content ) 548 { 549 if ( e.innerText ) 550 { 551 e.innerText = content; 552 } 553 else if ( e.nodeValue ) 554 { 555 e.nodeValue = content; 556 } 557 else if (e.textContent) 558 { 559 e.textContent = content; 560 } 561 562 } 563 function getTextContent( e ) 564 { 565 if ( e.innerText ) 566 { 567 return e.innerText; 568 } 569 else if ( e.nodeValue ) 570 { 571 return e.nodeValue; 572 } 573 else if ( e.textContent ) 574 { 575 return e.textContent; 576 } 577 else 578 { 579 return ""; 580 } 581 } 582 function getPrevNode( n ) 583 { 584 do 585 { 586 n = n.previousSibling; 587 } 588 while ( n && n.nodeType !== 1 && n.nodeType !== 3 ); 589 590 return n; 591 } 592 function findLastTextNode( $el, $top, excludeCurrent ) 593 { 594 var e = $el && $el[ 0 ], p; 595 if ( e ) 596 { 597 if ( !excludeCurrent ) 598 { 599 if ( e.nodeType === 3 ) 600 { 601 return e; 602 } 603 if ( $.trim( $el.text() ) ) 604 { 605 return findLastTextNode( $el.contents().last(), $top ); 606 } 607 } 608 p = getPrevNode( e ); 609 while ( !p ) 610 { 611 $el = $el.parent(); 612 if ( $el.is( $top ) || !$el.length ) 613 { 614 return false; 615 } 616 p = getPrevNode( $el[0] ); 617 } 618 if ( p ) 619 { 620 return findLastTextNode( $(p), $top ); 621 } 622 } 623 return false; 624 } 625 function getElement( e, $i ) 626 { 627 if ( !e ) 628 { 629 return false; 630 } 631 if ( typeof e === 'string' ) 632 { 633 e = $(e, $i); 634 return ( e.length ) 635 ? e 636 : false; 637 } 638 return !e.jquery 639 ? false 640 : e; 641 } 642 function getTrueInnerHeight( $el ) 643 { 644 var h = $el.innerHeight(), 645 a = [ 'paddingTop', 'paddingBottom' ]; 646 647 for ( var z = 0, l = a.length; z < l; z++ ) 648 { 649 var m = parseInt( $el.css( a[ z ] ), 10 ); 650 if ( isNaN( m ) ) 651 { 652 m = 0; 653 } 654 h -= m; 655 } 656 return h; 657 } 658 659 660 // override jQuery.html 661 var _orgHtml = $.fn.html; 662 $.fn.html = function( str ) 663 { 664 if ( str != undef && !$.isFunction( str ) && this.data( 'dotdotdot' ) ) 665 { 666 return this.trigger( 'update', [ str ] ); 667 } 668 return _orgHtml.apply( this, arguments ); 669 }; 670 671 672 // override jQuery.text 673 var _orgText = $.fn.text; 674 $.fn.text = function( str ) 675 { 676 if ( str != undef && !$.isFunction( str ) && this.data( 'dotdotdot' ) ) 677 { 678 str = $( '<div />' ).text( str ).html(); 679 return this.trigger( 'update', [ str ] ); 680 } 681 return _orgText.apply( this, arguments ); 682 }; 683 684 685})( 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.