1/****************************************************************************************************** 2 3 jQuery.ThreeDots 4 5 Author Jeremy Horn 6 Version 1.0.10 (Developed in Aptana Studio 1.5.1) 7 Date: 1/25/2010 8 9 Copyright (c) 2010 Jeremy Horn- jeremydhorn(at)gmail(dot)c0m | http://tpgblog.com 10 Dual licensed under MIT and GPL. 11 12 For more detailed documentation, including the latest updates and links to more usage and 13 examples, go to: 14 15 http://tpgblog.com/ThreeDots/ 16 17 KNOWN BUGS 18 None 19 20 DESCRIPTION 21 22 Sometimes the text ... 23 ... is too long ... 24 ... won't fit within the number of rows you have available. 25 26 Sometimes all you need is ... ThreeDots! 27 28 ThreeDots is a customizable jQuery plugin for the smart truncation of text. It shortens 29 provided text to fit specified dimensions and appends the desired ellipsis style 30 if/when truncation occurs. 31 32 For example --- 33 34 This: 35 There was once a brown fox 36 that liked to eat chocolate 37 pudding. 38 39 When restricted to 2 lines by ThreeDots, can become: 40 There was once a brown fox 41 that liked to eat ... 42 43 Or: 44 There was once a brown fox 45 that liked to (click for more) 46 47 ... and most any other permutation you desire. 48 49 50 BY DEFAULT 51 The three dots ellipsis ("...") is used, as shown in the prior example, and limits 52 text to a maximum of 2 lines. These and many other characteristics are fully customizable, 53 and fully itemized and explained below. 54 55 56 IMPLEMENTATION 57 58 HTML: <div class='text_here'><span class='ellipsis_text'>TEXT</span></div> 59 JS: $('.text_here').ThreeDots(); // USE DEFAULTS 60 $('.text_here2').ThreeDots({ { max_rows:3 }); 61 62 63 COMPATIBILITY 64 65 Tested in FF3.5, IE7, Chrome 66 With jQuery 1.3.x, 1.4 67 68 METHODS 69 70 ThreeDots() 71 72 When intialized the ThreeDots plugin creates and assigns the full set of provided text 73 to each container element as a publically accessible attribute, 'threedots'. Method 74 implementation supports chaining and returns jQuery object. 75 76 Note that to implement, the text that you wish to ellipsize must be wrapped in a span 77 assigned either the default class 'ellipsis_text' or other custom class of your 78 preference -- customizable via the options/settings. 79 80 If the text becomes truncated to fit within the constrained space defined by the 81 container element that holds the 'ellipsis_text' span then an additional span is 82 appended within the container object, and after the 'ellipsis_text' span. 83 84 Note, that the span class of 'threedots_ellipsis' can also be customized via the 85 options/settings and have it's own CSS/jQuery styles/actions/etc. applied to it as 86 desired. 87 88 If any of the specified settings are invalid or the 'ellipsis_text' span is missing 89 nothing will happen. 90 91 IMPORTANT: The horizontal constrains placed upon each row are controled by the 92 container object. The container object is the object specified in the 93 primary selector. 94 95 e.g. $('container_object').ThreeDots(); 96 97 So, remember to set container_object's WIDTH. 98 99 ThreeDots.update() 100 Refreshes the contents of the text within the target object inline with the 101 options provided. Note, that the current implementation of options/settings 102 are destructive. This means that whenever OPTIONS are specified they are 103 merged with the DEFAULT options and applied to the current object(s), and 104 destroy/override any previously specified options/settings. 105 106 example: 107 var obj = $('.text_here').ThreeDots(); // uses DEFAULT: max_rows = 2 108 obj.update({max_rows:3}); // update the text with max_rows = 3 109 110 CUSTOMIZATION 111 112 ThreeDots(OPTIONS) 113 e.g. $('.text_here').ThreeDots({ max_rows: 4 }); 114 115 116 valid_delimiters: character array of special characters upon which the text string may be broken up; 117 defines what characters can be used to express the bounds of a word 118 119 all elements in this array must be 1 character in length; any delimiter less than 120 or greater than 1 character will be ignored 121 122 123 ellipsis_string: defines what to display at the tail end of the text provided if the text becomes 124 truncated to fit within the space defined by the container object 125 126 127 max_rows: specifies the upper limit for the number of rows that the object's text can use 128 129 130 text_span_class: by default ThreeDots will look within the specified object(s) for a span 131 of the class 'ellipsis_text' 132 133 134 e_span_class: if an ellipsis_string is displayed at the tail end of the selected object's 135 text due to truncation of that text then it will be displayed wrapped within 136 a span associated with the class defined by e_span_class and immediately 137 following the text_span_class' span 138 139 140 whole_word: when fitting the provided text to the max_rows within the container object 141 this boolean setting defines whether or not the 142 143 if true 144 THEN don't truncate any words; ellipsis can ONLY be placed after 145 the last whole word that fits within the provided space, OR 146 147 if false 148 THEN maximuze the text within the provided space, allowing the
149 PARTIAL display of words before the ellipsis 150 151 152 allow_dangle: a dangling ellipsis is an ellipsis that typically occurs due to words that 153 are longer than a single row of text, resulting, upon text truncation in 154 the ellipsis being displayed on a row all by itself 155 156 if allow_dangle is set to false, whole_words is overridden ONLY in the 157 circumstances where a dangling ellipsis occurs and the displayed text 158 is adjusted to minimize the occurence of such dangling 159 160 161 alt_text_e: alt_text_e is a shortcut to enabling the user of the product that 162 made use of ThreeDots to see the full text, prior to truncation 163 164 if the value is set to true, then the ellipsis span's title property 165 is set to the full, original text (pre-truncation) 166 167 168 alt_text_t: alt_text_t is a shortcut to enabling the user of the product that 169 made use of ThreeDots to see the full text, prior to truncation 170 171 if the value is set to true AND the ellipsis is displayed, then the 172 text span's title property is set to the full, original text 173 (pre-truncation) 174 175 176 MORE 177 178 For latest updates and links to more usage and examples, go to: 179 http://tpgblog.com/ThreeDots/ 180 181 FUTURE NOTE 182 183 Do not write any code dependent on the c_settings variable. If you don't know what this is 184 cool -- you don't need to. ;-) c_settings WILL BE DEPRECATED. 185 186 Further optimizations in progress... 187 188******************************************************************************************************/ 189 190 191(function($) { 192 193 /********************************************************************************** 194 195 METHOD 196 ThreeDots {PUBLIC} 197 198 DESCRIPTION 199 ThreeDots method constructor 200 201 allows for the customization of ellipsis, delimiters, etc., and smart 202 truncation of provided objects' text 203 204 e.g. $(something).ThreeDots(); 205 206 **********************************************************************************/ 207 208 $.fn.ThreeDots = function(options) { 209 var return_value = this; 210 211 // check for new & valid options 212 if ((typeof options == 'object') || (options == undefined)) { 213 $.fn.ThreeDots.the_selected = this; 214 215 var return_value = $.fn.ThreeDots.update(options); 216 217 } 218 219 return return_value; 220 }; 221 222 223 /********************************************************************************** 224 225 METHOD 226 ThreeDots.update {PUBLIC} 227 228 DESCRIPTION 229 applies the core logic of ThreeDots 230 231 allows for the customization of ellipsis, delimiters, etc., and smart 232 truncation of provided objects' text 233 234 updates the objects' visible text to fit within its container(s) 235 236 TODO 237 instead of having all options/settings calls be constructive have 238 settings associated w/ object returned also accessible from HERE 239 [STATIC settings, associated w/ the initial call] 240 241 **********************************************************************************/ 242 243 $.fn.ThreeDots.update = function(options) { 244 // initialize local variables 245 var curr_this, last_word = null; 246 var lineh, paddingt, paddingb, innerh, temp_height; 247 var curr_text_span, lws; /* last word structure */ 248 var last_text, three_dots_value, last_del; 249 250 // check for new & valid options 251 if ((typeof options == 'object') || (options == undefined)) { 252 253 // then update the settings 254 // CURRENTLY, settings are not CONSTRUCTIVE, but merged with the DEFAULTS every time 255 $.fn.ThreeDots.c_settings = $.extend({}, $.fn.ThreeDots.settings, options); 256 var max_rows = $.fn.ThreeDots.c_settings.max_rows; 257 if (max_rows < 1) { 258 return $.fn.ThreeDots.the_selected; 259 } 260 261 // make sure at least 1 valid delimiter 262 var valid_delimiter_exists = false; 263 jQuery.each($.fn.ThreeDots.c_settings.valid_delimiters, function(i, curr_del) { 264 if (((new String(curr_del)).length == 1)) { 265 valid_delimiter_exists = true; 266 } 267 }); 268 if (valid_delimiter_exists == false) { 269 return $.fn.ThreeDots.the_selected; 270 } 271 272 // process all provided objects 273 $.fn.ThreeDots.the_selected.each(function() { 274 275 // element-specific code here 276 curr_this = $(this); 277 278 // obtain the text span 279 if ($(curr_this).children('.'+$.fn.ThreeDots.c_settings.text_span_class).length == 0) {
280 // if span doesnt exist, then go to next 281 return true; 282 } 283 curr_text_span = $(curr_this).children('.'+$.fn.ThreeDots.c_settings.text_span_class).get(0); 284 285 // pre-calc fixed components of num_rows 286 var nr_fixed = num_rows(curr_this, true); 287 288 // remember where it all began so that we can see if we ended up exactly where we started 289 var init_text_span = $(curr_text_span).text(); 290 291 // preprocessor 292 the_bisector(curr_this, curr_text_span, nr_fixed); 293 var init_post_b = $(curr_text_span).text(); 294 295 // if the object has been initialized, then user must be calling UPDATE 296 // THEREFORE refresh the text area before re-operating 297 if ((three_dots_value = $(curr_this).attr('threedots')) != undefined) { 298 $(curr_text_span).text(three_dots_value); 299 $(curr_this).children('.'+$.fn.ThreeDots.c_settings.e_span_class).remove(); 300 } 301 302 last_text = $(curr_text_span).text(); 303 if (last_text.length <= 0) { 304 last_text = ''; 305 } 306 $(curr_this).attr('threedots', init_text_span); 307 308 if (num_rows(curr_this, nr_fixed) > max_rows) { 309 // append the ellipsis span & remember the original text 310 curr_ellipsis = $(curr_this).append('<span style="white-space:nowrap" class="' 311 + $.fn.ThreeDots.c_settings.e_span_class + '">' 312 + $.fn.ThreeDots.c_settings.ellipsis_string 313 + '</span>'); 314 315 // remove 1 word at a time UNTIL max_rows 316 while (num_rows(curr_this, nr_fixed) > max_rows) { 317 318 lws = the_last_word($(curr_text_span).text());// HERE 319 $(curr_text_span).text(lws.updated_string); 320 last_word = lws.word; 321 last_del = lws.del; 322 323 if (last_del == null) { 324 break; 325 } 326 } // while (num_rows(curr_this, nr_fixed) > max_rows) 327 328 // check for super long words 329 if (last_word != null) { 330 var is_dangling = dangling_ellipsis(curr_this, nr_fixed); 331 332 if ((num_rows(curr_this, nr_fixed) <= max_rows - 1) 333 || (is_dangling) 334 || (!$.fn.ThreeDots.c_settings.whole_word)) { 335 336 last_text = $(curr_text_span).text(); 337 if (lws.del != null) { 338 $(curr_text_span).text(last_text + last_del); 339 } 340 341 if (num_rows(curr_this, nr_fixed) > max_rows) { 342 // undo what i just did and stop 343 $(curr_text_span).text(last_text); 344 } else { 345 // keep going 346 $(curr_text_span).text($(curr_text_span).text() + last_word); 347 348 // break up the last word IFF (1) word is longer than a line, OR (2) whole_word == false 349 if ((num_rows(curr_this, nr_fixed) > max_rows + 1) 350 || (!$.fn.ThreeDots.c_settings.whole_word) 351 || (init_post_b == last_word) 352 || is_dangling) { 353 // remove 1 char at a time until it all fits 354 while ((num_rows(curr_this, nr_fixed) > max_rows)) { 355 if ($(curr_text_span).text().length > 0) { 356 $(curr_text_span).text( 357 $(curr_text_span).text().substr(0, $(curr_text_span).text().length - 1) 358 ); 359 } else { 360 /* 361 there is no hope for you; you are crazy; 362 either pick a shorter ellipsis_string OR 363 use a wider object --- geeze! 364 */ 365 break; 366 } 367 } 368 } 369 } 370 } 371 } 372 } 373 374 // if nothing has changed, remove the ellipsis 375 if (init_text_span == $($(curr_this).children('.' + $.fn.ThreeDots.c_settings.text_span_class).get(0)).text()) { 376 $(curr_this).children('.' + $.fn.ThreeDots.c_settings.e_span_class).remove(); 377 } else { 378 // only add any title text if the ellipsis is visible 379 if (($(curr_this).children('.' + $.fn.ThreeDots.c_settings.e_span_class)).length > 0) { 380 if ($.fn.ThreeDots.c_settings.alt_text_t) { 381 $(curr_this).children('.' + $.fn.ThreeDots.c_settings.text_span_class).attr('title', init_text_span); 382 } 383 384 if ($.fn.ThreeDots.c_settings.alt_text_e) { 385 $(curr_this).children('.' + $.fn.ThreeDots.c_settings.e_span_class).attr('title', init_text_span); 386 } 387 388 } 389 } 390 }); // $.fn.ThreeDots.the_selected.each(function() 391 } 392 393 return $.fn.ThreeDots.the_selected; 394 }; 395 396 397 /********************************************************************************** 398 399 METHOD 400 ThreeDots.settings {PUBLIC} 401 402 DESCRIPTION 403 data structure containing the max_rows, ellipsis string, and other 404 behavioral settings 405 406 can be directly accessed by '$.fn.ThreeDots.settings = ...... ;' 407 408 **********************************************************************************/ 409 410 $.fn.ThreeDots.settings = { 411 valid_delimiters: [' ', ',', '.'], // what defines the bounds of a word to you? 412 ellipsis_string: '...', 413 max_rows: 2, 414 text_span_class: 'ellipsis_text', 415 e_span_class: 'threedots_ellipsis', 416 whole_word: true, 417 allow_dangle: false, 418 alt_text_e: false, // if true, mouse over of ellipsis displays the full text 419 alt_text_t: false // if true & if ellipsis displayed, mouse over of text displays the full text 420 }; 421 422 423 /********************************************************************************** 424 425 METHOD 426 dangling_ellipsis {private} 427 428 DESCRIPTION 429 determines whether or not the currently calculated ellipsized text 430 is displaying a dangling ellipsis (= an ellipsis on a line by itself) 431 432 returns true if ellipsis is dangling, otherwise false 433 434 **********************************************************************************/ 435 436 function dangling_ellipsis(obj, nr_fixed){ 437 if ($.fn.ThreeDots.c_settings.allow_dangle == true) { 438 return false; // why do when no doing need be done? 439 } 440 441 // initialize variables 442 var ellipsis_obj = $(obj).children('.'+$.fn.ThreeDots.c_settings.e_span_class).get(0); 443 var remember_display = $(ellipsis_obj).css('display'); 444 var num_rows_before = num_rows(obj, nr_fixed); 445 446 // temporarily hide ellipsis 447 $(ellipsis_obj).css('display','none'); 448 var num_rows_after = num_rows(obj, nr_fixed); 449 450 // restore ellipsis 451 $(ellipsis_obj).css('display',remember_display); 452 453 if (num_rows_before >
453 num_rows_after) { 454 return true; // ASSUMPTION: removing the ellipsis changed the height 455 // THEREFORE the ellipsis was on a row all by its lonesome 456 } else { 457 return false; // nothing dangling here 458 } 459 } 460 461 462 /********************************************************************************** 463 464 METHOD 465 num_rows {private} 466 467 DESCRIPTION 468 returns the number of rows/lines that the current object's text covers if 469 cstate is an object 470 471 this function can be initially called to pre-calculate values that will 472 stay fixed throughout the truncation process for the current object so 473 that the values do not have to be called every time; to do this the 474 num_rows function is called with a boolean value within the cstate 475 476 when boolean cstate, an object is returned containing padding and line 477 height information that is then passed in as the cstate object on 478 subsequent calls to the function 479 480 **********************************************************************************/ 481 482 function num_rows(obj, cstate){ 483 var the_type = typeof cstate; 484 485 if ( (the_type == 'object') 486 || (the_type == undefined) ) { 487 488 // do the math & return 489 return $(obj).height() / cstate.lh; 490 491 } else if (the_type == 'boolean') { 492 var lineheight = lineheight_px($(obj)); 493 494 return { 495 lh: lineheight 496 }; 497 } 498 } 499 500 501 /********************************************************************************** 502 503 METHOD 504 the_last_word {private} 505 506 DESCRIPTION 507 return a data structure containing... 508 509 [word] the last word within the specified text defined 510 by the specified valid_delimiters, 511 [del] the delimiter occurring directly before the 512 word, and 513 [updated_string] the updated text minus the last word 514 515 [del] is null if the last word is the first and/or only word in the text 516 string 517 518 **********************************************************************************/ 519 520 function the_last_word(str){ 521 var temp_word_index; 522 var v_del = $.fn.ThreeDots.c_settings.valid_delimiters; 523 524 // trim the string 525 str = jQuery.trim(str); 526 527 // initialize variables 528 var lastest_word_idx = -1; 529 var lastest_word = null; 530 var lastest_del = null; 531 532 // for all given delimiters, determine which delimiter results in the smallest word cut 533 jQuery.each(v_del, function(i, curr_del){ 534 if (((new String(curr_del)).length != 1) 535 || (curr_del == null)) { // implemented to handle IE NULL condition; if only typeof could say CHAR :( 536 return false; // INVALID delimiter; must be 1 character in length 537 } 538 539 var tmp_word_index = str.lastIndexOf(curr_del); 540 if (tmp_word_index != -1) { 541 if (tmp_word_index > lastest_word_idx) { 542 lastest_word_idx = tmp_word_index; 543 lastest_word = str.substring(lastest_word_idx+1); 544 lastest_del = curr_del; 545 } 546 } 547 }); 548 549 // return data structure of word reduced string and the last word 550 if (lastest_word_idx > 0) { 551 return { 552 updated_string: jQuery.trim(str.substring(0, lastest_word_idx/*-1*/)), 553 word: lastest_word, 554 del: lastest_del 555 }; 556 } else { // the lastest word 557 return { 558 updated_string: '', 559 word: jQuery.trim(str), 560 del: null 561 }; 562 } 563 } 564 565 566 /********************************************************************************** 567 568 METHOD 569 lineheight_px {private} 570 571 DESCRIPTION 572 returns the line height of a row of the provided text (within the text 573 span) in pixels 574 575 **********************************************************************************/ 576 577 function lineheight_px(obj) { 578 // shhhh... show 579 $(obj).append("<div id='temp_ellipsis_div' style='position:absolute; visibility:hidden'>H</div>"); 580 // measure 581 var temp_height = $('#temp_ellipsis_div').height(); 582 // cut 583 $('#temp_ellipsis_div').remove(); 584 585 return temp_height; 586 } 587 588 /********************************************************************************** 589 590 METHOD 591 the_bisector (private) 592 593 DESCRIPTION 594 updates the target objects current text to shortest overflowing str
594ing 595 length (if overflowing is occurring) by adding/removing halves (like 596 binary search) 597 598 because... 599 taking some bigger steps at the beginning should save us some real 600 time in the end 601 602 **********************************************************************************/ 603 604 function the_bisector(obj, curr_text_span, nr_fixed){ 605 var init_text = $(curr_text_span).text(); 606 var curr_text = init_text; 607 var max_rows = $.fn.ThreeDots.c_settings.max_rows; 608 var front_half, back_half, front_of_back_half, middle, back_middle; 609 var start_index; 610 611 if (num_rows(obj, nr_fixed) <= max_rows) { 612 // do nothing 613 return; 614 } else { 615 // zero in on the solution 616 start_index = 0; 617 curr_length = curr_text.length; 618 619 curr_middle = Math.floor((curr_length - start_index) / 2); 620 front_half = init_text.substring(start_index, start_index+curr_middle); 621 back_half = init_text.substring(start_index + curr_middle); 622 623 while (curr_middle != 0) { 624 $(curr_text_span).text(front_half); 625 626 if (num_rows(obj, nr_fixed) <= (max_rows)) { 627 // text = text + front half of back-half 628 back_middle = Math.floor(back_half.length/2); 629 front_of_back_half = back_half.substring(0, back_middle); 630 631 start_index = front_half.length; 632 curr_text = front_half+front_of_back_half; 633 curr_length = curr_text.length; 634 635 $(curr_text_span).text(curr_text); 636 } else { 637 // text = front half (which it already is) 638 curr_text = front_half; 639 curr_length = curr_text.length; 640 } 641 642 curr_middle = Math.floor((curr_length - start_index) / 2); 643 front_half = init_text.substring(0, start_index+curr_middle); 644 back_half = init_text.substring(start_index + curr_middle); 645 } 646 } 647 } 648 649})(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.