1/*! 2 * jQuery.ellipsis 3 * http://github.com/jjenzz/jquery.ellipsis 4 * -------------------------------------------------------------------------- 5 * Copyright (c) 2013 J. Smith (@jjenzz) 6 * Dual licensed under the MIT and GPL licenses: 7 * http://www.opensource.org/licenses/mit-license.php 8 * http://www.gnu.org/licenses/gpl.html 9 * 10 * adds a class to the last 'allowed' line of text so you can apply 11 * text-overflow: ellipsis; 12 */ 13(function (factory) { 14 'use strict'; 15 16 if (typeof define === 'function' && define.amd) { 17 // AMD. register as an anonymous module 18 define(['jquery'], factory); 19 } else { 20 // browser globals 21 factory(jQuery); 22 } 23}(function ($) { 24 'use strict'; 25 var namespace = 'ellipsis', 26 span = '<span style="white-space: nowrap;">', 27 defaults = { 28 lines: 'auto', 29 ellipClass: 'ellip', 30 responsive: false 31 }; 32 33 /** 34 * Ellipsis() 35 * -------------------------------------------------------------------------- 36 * @param {Node} el 37 * @param {Object} opts 38 */ 39 function Ellipsis(el, opts) { 40 var base = this, 41 currLine = 0, 42 lines = [], 43 setStartEllipAt, 44 startEllipAt, 45 resizeTimer, 46 currOffset, 47 lineHeight, 48 contHeight, 49 words, 50 htmlEntities; 51 52 // List of HTML entities for escaping. 53 htmlEntities = { 54 '&': '&', 55 '<': '<', 56 '>': '>', 57 '"': '"', 58 "'": ''', 59 '`': '`', 60 '/': '/', 61 62 }; 63 64 base.$cont = $(el); 65 base.opts = $.extend({}, defaults, opts); 66 67 /** 68 * create() happens once when 69 * instance is created 70 */ 71 function create() { 72 base.text = base.$cont.text(); 73 base.opts.ellipLineClass = base.opts.ellipClass + '-line'; 74 75 base.$el = $('<span class="' + base.opts.ellipClass + '" />'); 76 base.$el.text(base.text); 77 78 base.$cont.empty().append(base.$el); 79 80 init(); 81 } 82 83 /** 84 * init() 85 */ 86 function init() { 87 // if they only want one line just add 88 // the class and do nothing else 89 if (typeof base.opts.lines === 'number' && base.opts.lines < 2) { 90 base.$el.addClass(base.opts.ellipLineClass); 91 return; 92 } 93 94 contHeight = base.$cont.height(); 95 96 // if they only want to ellipsis the overflow 97 // then do nothing if there is no overflow 98 if (base.opts.lines === 'auto' && base.$el.prop('scrollHeight') <= contHeight) { 99 return; 100 } 101 102 if (!setStartEllipAt) { 103 return; 104 } 105 106 // create an array of words from our string 107 words = $.trim(escapeText(base.text)).split(/\s+/); 108 109 // wrap each word in a span and temporarily append to the DOM 110 base.$el.html(span + words.join('</span> ' + span) + '</span>'); 111 112 // loop through words to determine which word the 113 // ellipsis container should start from (need to 114 // re-query spans from DOM so we can get their offset) 115 base.$el.find('span').each(setStartEllipAt); 116 117 // startEllipAt could be 0 so make sure we're 118 // checking undefined instead of falsey 119 if (startEllipAt != null) { 120 updateText(startEllipAt); 121 } 122 } 123 124 /** 125 * updateText() updates the text in the DOM 126 * with a span around the line that needs 127 * to be truncated 128 * 129 * @param {Number} i 130 */ 131 function updateText(nth) { 132 // add a span that wraps from nth 133 // word to the end of the string 134 words[nth] = '<span class="' + base.opts.ellipLineClass + '">' + words[nth]; 135 words.push('</span>'); 136 137 // update the DOM with 138 // our new string/markup 139 base.$el.html(words.join(' '));
140 } 141 142 function escapeText(text) { 143 return String(text).replace(/[&<>"'\/]/g, function (s) { 144 return htmlEntities[s]; 145 }); 146 } 147 148 // only define the method if it's required 149 if (base.opts.lines === 'auto') { 150 151 /** 152 * setStartEllipByHeight() sets the start 153 * position to the first word in the last 154 * line of the element that doesn't overflow 155 * 156 * @param {Number} ihtmlEntities 157 * @param {Node} word 158 */ 159 var setStartEllipByHeight = function (i, word) { 160 var $word = $(word), 161 top = $word.position().top; 162 163 lineHeight = lineHeight || $word.height(); 164 165 if (top === currOffset) { 166 // if it's top matches currOffset 167 // then it's on the same line 168 // as the previous word 169 lines[currLine].push($word); 170 } else { 171 // otherwise we're 172 // on a new line 173 currOffset = top; 174 currLine += 1; 175 lines[currLine] = [$word]; 176 } 177 178 // if the bottom of the word is outside 179 // the element (overflowing) then 180 // stop looping and set startEllipAt to 181 // the first word in the previous line 182 if (top + lineHeight > contHeight) { 183 startEllipAt = i - lines[currLine - 1].length; 184 return false; 185 } 186 }; 187 188 setStartEllipAt = setStartEllipByHeight; 189 } 190 191 // only define the method if it's required 192 if (typeof base.opts.lines === 'number' && base.opts.lines > 1) { 193 194 /** 195 * setStartEllipByLine() sets the start 196 * position to the first word in the line 197 * that was passed to opts. This forces 198 * the ellipsis on a specific line 199 * regardless of overflow 200 * 201 * @param {Number} i 202 * @param {Node} word 203 */ 204 var setStartEllipByLine = function (i, word) { 205 var $word = $(word), 206 top = $word.position().top; 207 208 // if top isn't currOfset 209 // then we're on a new line 210 if (top !== currOffset) { 211 currOffset = top; 212 currLine += 1; 213 } 214 215 // if the word's currLine is equal 216 // to the line limit passed via options 217 // then start ellip from this 218 // word and stop looping 219 if (currLine === base.opts.lines) { 220 startEllipAt = i; 221 return false; 222 } 223 }; 224 225 setStartEllipAt = setStartEllipByLine; 226 } 227 228 // only bind to window resize if required 229 if (base.opts.responsive) { 230 231 /** 232 * resize() resets necessary vars 233 * and content and then re-initialises 234 * the Ellipsis script 235 */ 236 var resize = function () { 237 lines = []; 238 currLine = 0; 239 currOffset = null; 240 startEllipAt = null; 241 base.$el.html(escapeText(base.text)); 242 243 clearTimeout(resizeTimer); 244 resizeTimer = setTimeout(init, 100); 245 }; 246 247 $(window).on('resize.' + namespace, resize); 248 } 249 250 // start 'er up 251 create(); 252 } 253 254 $.fn[namespace] = function (opts) { 255 return this.each(function () { 256 try { 257 $(this).data(namespace, (new Ellipsis(this, opts))); 258 } catch (err) { 259 if (window.console) { 260 console.error(namespace + ': ' + err); 261 } 262 } 263 }); 264 }; 265 266}));
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.