1/**! 2 * trunk8 v1.3.3 3 * https://github.com/rviscomi/trunk8 4 * 5 * Copyright 2012 Rick Viscomi 6 * Released under the MIT License. 7 * 8 * Date: September 26, 2012 9 */ 10 11(function (root, factory) { 12 if (typeof define === 'function' && define.amd) { 13 define(['jquery'], factory); 14 } else if (typeof module === 'object' && module.exports) { 15 factory(require('jquery')); 16 } else { 17 factory(root.jQuery); 18 } 19}(this, function ($) { 20 var methods, 21 utils, 22 SIDES = { 23 /* cen...ter */ 24 center: 'center', 25 /* ...left */ 26 left: 'left', 27 /* right... */ 28 right: 'right' 29 }, 30 WIDTH = { 31 auto: 'auto' 32 }; 33 34 function trunk8(element) { 35 this.$element = $(element); 36 this.original_text = $.trim(this.$element.html()); 37 this.settings = $.extend({}, $.fn.trunk8.defaults); 38 } 39 40 trunk8.prototype.updateSettings = function (options) { 41 this.settings = $.extend(this.settings, options); 42 }; 43 44 function stripHTML(html) { 45 var tmp = document.createElement("DIV"); 46 tmp.innerHTML = html; 47 48 if (typeof tmp.textContent != 'undefined') { 49 return tmp.textContent; 50 } 51 52 return tmp.innerText 53 } 54 55 function getHtmlArr(str) { 56 /* Builds an array of strings and designated */ 57 /* HTML tags around them. */ 58 if (stripHTML(str) === str) { 59 return str.split(/\s/g); 60 } 61 var allResults = [], 62 reg = /<([a-z]+)([^<]*)(?:>([\S\s]*?(?!<\1>))<\/\1>|\s+\/>)(['.?!,]*)|((?:[^<>\s])+['.?!,]*\w?|<br\s?\/?>)/ig, 63 outArr = reg.exec(str), 64 lastI, 65 ind; 66 while (outArr && lastI !== reg.lastIndex) { 67 lastI = reg.lastIndex; 68 if (outArr[5]) { 69 allResults.push(outArr[5]); 70 } else if (outArr[1]) { 71 allResults.push({ 72 tag: outArr[1], 73 attribs: outArr[2], 74 content: outArr[3], 75 after: outArr[4] 76 }); 77 } 78 outArr = reg.exec(str); 79 } 80 for (ind = 0; ind < allResults.length; ind++) { 81 if (typeof allResults[ind] !== 'string' && 82 allResults[ind].content) { 83 allResults[ind].content = getHtmlArr(allResults[ind].content); 84 } 85 } 86 return allResults; 87 } 88 89 function rebuildHtmlFromBite(bite, htmlObject, fill) { 90 // Take the processed bite after binary-search 91 // truncated and re-build the original HTML 92 // tags around the processed string. 93 bite = bite.replace(fill, ''); 94 95 var biteHelper = function(contentArr, tagInfo) { 96 var retStr = '', 97 content, 98 biteContent, 99 biteLength, 100 nextWord, 101 i; 102 for (i = 0; i < contentArr.length; i++) { 103 content = contentArr[i]; 104 biteLength = $.trim(bite).split(' ').length; 105 if ($.trim(bite).length) { 106 if (typeof content === 'string') { 107 if (!/<br\s*\/?>/i.test(content)) { 108 if (biteLength === 1 && $.trim(bite).length <= content.length) { 109 content = bite; 110 // We want the fill to go inside of the last HTML 111 // element if the element is a container. 112 if (tagInfo === 'p' || tagInfo === 'div') { 113 content += fill; 114 } 115 bite = ''; 116 } else { 117 bite = bite.replace(content, ''); 118 } 119 } 120 retStr += $.trim(content) + ((i === contentArr.length-1 || biteLength <= 1) ? '' : ' '); 121 } else { 122 biteContent = biteHelper(content.content, content.tag); 123 if (content.after) bite = bite.replace(content.after, ''); 124 if (biteContent) { 125 if (!content.after) content.after = ' '; 126 retStr += '<'+content.tag+content.attribs+'>'+biteContent+'</'+content.tag+'>' + content.after; 127 } 128 } 129 } 130 } 131 return retStr; 132 }, 133 htmlResults = biteHelper(htmlObject); 134 135 // Add fill if doesn't exist. This will place it outside the HTML elements. 136 if (htmlResults.slice(htmlResults.length - fill.length) !== fill) { 137 htmlResults += fill; 138 } 139 140 return htmlResults; 141 } 142 143 function truncate() { 144 var data = this.data('trunk8'), 145 settings = data.settings, 146 width = settings.width, 147 side = settings.side, 148 fill = settings.fill, 149 parseHTML = settings.parseHTML, 150 line_height = utils.getLineHeight(this) * settings.lines, 151 str = data.original_text, 152 length = str.length, 153 max_bite = '', 154 lower, upper, 155 bite_size, 156 bite, 157 text, 158 htmlObject; 159 160 /* Reset the field to the original string. */ 161 this.html(str); 162 text = this.text(); 163
164 /* If string has HTML and parse HTML is set, build */ 165 /* the data struct to house the tags */ 166 if (parseHTML && stripHTML(str) !== str) { 167 htmlObject = getHtmlArr(str); 168 str = stripHTML(str); 169 length = str.length; 170 } 171 172 if (width === WIDTH.auto) { 173 /* Assuming there is no "overflow: hidden". */ 174 if (this.height() <= line_height) { 175 /* Text is already at the optimal trunkage. */ 176 return; 177 } 178 179 /* Binary search technique for finding the optimal trunkage. */ 180 /* Find the maximum bite without overflowing. */ 181 lower = 0; 182 upper = length - 1; 183 184 while (lower <= upper) { 185 bite_size = lower + ((upper - lower) >> 1); 186 187 bite = utils.eatStr(str, side, length - bite_size, fill); 188 189 if (parseHTML && htmlObject) { 190 bite = rebuildHtmlFromBite(bite, htmlObject, fill); 191 } 192 193 this.html(bite); 194 195 /* Check for overflow. */ 196 if (this.height() > line_height) { 197 upper = bite_size - 1; 198 } 199 else { 200 lower = bite_size + 1; 201 202 /* Save the bigger bite. */ 203 max_bite = (max_bite.length > bite.length) ? max_bite : bite; 204 } 205 } 206 207 /* Reset the content to eliminate possible existing scroll bars. */ 208 this.html(''); 209 210 /* Display the biggest bite. */ 211 this.html(max_bite); 212 213 if (settings.tooltip) { 214 this.attr('title', text); 215 } 216 } 217 else if (!isNaN(width)) { 218 bite_size = length - width; 219 220 bite = utils.eatStr(str, side, bite_size, fill); 221 222 this.html(bite); 223 224 if (settings.tooltip) { 225 this.attr('title', str); 226 } 227 } 228 else { 229 $.error('Invalid width "' + width + '".'); 230 return; 231 } 232 settings.onTruncate(); 233 } 234 235 methods = { 236 init: function (options) { 237 return this.each(function () { 238 var $this = $(this), 239 data = $this.data('trunk8'); 240 241 if (!data) { 242 $this.data('trunk8', (data = new trunk8(this))); 243 } 244 245 data.updateSettings(options); 246 247 truncate.call($this); 248 }); 249 }, 250 251 /** Updates the text value of the elements while maintaining truncation. */ 252 update: function (new_string) { 253 return this.each(function () { 254 var $this = $(this); 255 256 /* Update text. */ 257 if (new_string) { 258 $this.data('trunk8').original_text = new_string; 259 } 260 261 /* Truncate accordingly. */ 262 truncate.call($this); 263 }); 264 }, 265 266 revert: function () { 267 return this.each(function () { 268 /* Get original text. */ 269 var text = $(this).data('trunk8').original_text; 270 271 /* Revert element to original text. */ 272 $(this).html(text); 273 }); 274 }, 275 276 /** Returns this instance's settings object. NOT CHAINABLE. */ 277 getSettings: function () { 278 return $(this.get(0)).data('trunk8').settings; 279 } 280 }; 281 282 utils = { 283 /** Replaces [bite_size] [side]-most chars in [str] with [fill]. */ 284 eatStr: function (str, side, bite_size, fill) { 285 var length = str.length, 286 key = utils.eatStr.generateKey.apply(null, arguments), 287 half_length, 288 half_bite_size; 289 290 /* If the result is already in the cache, return it. */ 291 if (utils.eatStr.cache[key]) { 292 return utils.eatStr.cache[key]; 293 } 294 295 /* Common error handling. */ 296 if ((typeof str !== 'string') || (length === 0)) { 297 $.error('Invalid source string "' + str + '".'); 298 } 299 if ((bite_size < 0) || (bite_size > length)) { 300 $.error('Invalid bite size "' + bite_size + '".'); 301 } 302 else if (bite_size === 0) { 303 /* No bite should show no truncation. */ 304 return str; 305 } 306 if (typeof (fill + '') !== 'string') { 307 $.error('Fill unable to be converted to a string.'); 308 } 309 310 /* Compute the result, store it in the cache, and return it. */ 311 switch (side) { 312 case SIDES.right: 313 /* str... */ 314 return utils.eatStr.cache[key] = 315 $.trim(str.substr(0, length - bite_size)) + fill; 316 317 case SIDES.left: 318 /* ...str */ 319 return utils.eatStr.cache[key] = 320 fill + $.trim(str.substr(bite_size)); 321 322 case SIDES.center: 323 /* Bit-shift to the right by one === Math.floor(x / 2) */ 324 half_length = length >> 1; // halve the length 325 half_bite_size = bite_size >> 1; // halve the bite_size 326 327 /* st...r */ 328 return utils.eatStr.cache[key] = 329 $.trim(utils.eatStr(str.substr(0, length - half_length), SIDES.right, bite_size - half_bite_size, '')) + 330 fill + 331 $.trim(utils.eatStr(str.substr(length - half_length), SIDES.left, half_bite_size, '')); 332 333 default: 334 $.error('Invalid side "' + side + '".'); 335 } 336 }, 337 338 getLineHeight: function (elem) { 339 var floats = $(elem).css('float'); 340 if (floats !== 'none') { 341 $(elem).css('float', 'none'); 342 } 343 var pos = $(elem).css('position'); 344 if (pos === 'absolute') { 345 $(elem).css('position', 'static'); 346 } 347 348 var html = $(elem).html(), 349 wrapper_id = 'line-height-test', 350 line_height; 351 352 /* Set the content to a small single character and wrap. */ 353 $(elem).html('i').wrap('<div id="' + wrapper_id + '" />'); 354 355 /* Calculate the line height by measuring the wrapper.*/ 356 line_height = $('#' + wrapper_id).innerHeight(); 357 358 /* Remove the wrapper and reset the content. */ 359 $(elem).html(html).css({ 'float': floats, 'position': pos }).unwrap(); 360 361 return line_height; 362 } 363 }; 364 365 utils.eatStr.cache = {}; 366 utils.eatStr.generateKey = function () { 367 return Array.prototype.join.call(arguments, ''); 368 }; 369 370 $.fn.trunk8 = function (method) { 371 if (methods[method]) { 372 return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); 373 } 374 else if (typeof method === 'object' || !method) { 375 return methods.init.apply(this, arguments); 376 } 377 else { 378 $.error('Method ' + method + ' does not exist on jQuery.trunk8'); 379 } 380 }; 381 382 /* Default trunk8 settings. */ 383 $.fn.trunk8.defaults = { 384 fill: '…', 385 lines: 1, 386 side: SIDES.right, 387 tooltip: true, 388 width: WIDTH.auto,
389 parseHTML: false, 390 onTruncate: function () {} 391 }; 392}));
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.