1/** 2 * @Project Ellipsis plugin 3 * @Description jquery plugin that excerpt the text at dedecated lines or 4 * dedicated number of chars 5 * 6 * @Author Mohamed Hassan 7 * @Author_url http://mohamedhassan.me 8 * @License MIT 9 */ 10;(function($, window, document, undefined){ 11 'use strict'; 12 // define the plugin name and the default options 13 const PLUGIN_NAME = 'ellipsis'; 14 const VERSION = '0.1.6'; 15 16 17 /** 18 * the default options of Ellipsis 19 * @type {Object} 20 */ 21 const DEFAULTS = { 22 type: 'lines', 23 count: 3 24 }; 25 26 27 28 /** 29 * different custom events shoud 30 * @type { Object } 31 */ 32 const EVENTS = { 33 namespace: 'ellispsis', 34 initialize: 'initialize.ellipsis', 35 initialized: 'initialized.ellipsis', 36 update: 'update.ellipsis', 37 updated: 'updated.ellipsis', 38 excerpt: 'excerpt.ellipsis', 39 excerpted: 'excerpted.ellipsis' 40 }; 41 42 43 44 /** 45 * Ellipsis class that excerpt the text of an attached element depending 46 * on number of lines / characters 47 * @param {HTMLElement} element attached element to excerpt it's text 48 * @param {Object} options list of custom options 49 */ 50 class Ellipsis{ 51 constructor(element, options = {}) { 52 this.element = $(element); 53 this.options = Object.assign({}, Ellipsis.DEFAULTS, options); 54 this.text = this.element.text(); 55 this._resizeTimeout = null; 56 this._resizeHandler = this._updateOnResize.bind(this); 57 58 this.init(); 59 } 60 61 62 63 /*========================================================================= 64 * PUBLIC 65 *========================================================================*/ 66 67 /** 68 * Public init() initialize the plugin and do logical stuff 69 */ 70 init() { 71 this.element.trigger(EVENTS.initialize); 72 73 this.element.trigger(EVENTS.excerpt); 74 if(this.options.type === 'lines') { 75 this._excerptLines(this.options.count); 76 } else { 77 this._excerptChars(this.options.count); 78 } 79 80 if(this.options.type === 'lines') { 81 $(window).on('resize', this.element.selector, this._resizeHandler); 82 } 83 this.element.trigger(EVENTS.initialized); 84 } 85 86 87 88 /** 89 * updating the ellipsed text. 90 */ 91 update() { 92 this.element.trigger(EVENTS.update); 93 94 if(this.options.type === 'lines') 95 this._excerptLines(this.options.count); 96 else 97 this._excerptChars(this.options.count); 98 99 //later 100 this.element.trigger(EVENTS.updated); 101 return true; 102 } 103 104 105 106 /** 107 * reset ellipsis instance options 108 * if any change is needed later. 109 * 110 * @param {Object} options text/type/count. 111 */ 112 reset(options) { 113 if(options.text) this.text = options.text; 114 if(options.type) this.options.type = options.type; 115 if(options.count) this.options.count = options.count; 116 117 if(Object.keys(options).length > 0) { 118 this.element.text(this.text); 119 this.update(); 120 } 121 } 122 123 124 125 destroy() { 126 $(window).off('resize', this._resizeHandler); 127 this.element.text(this.text); 128 this.element.off(EVENTS.namespace); 129 } 130 131 132 133 /*========================================================================= 134 * private Methods 135 *========================================================================*/ 136 /** 137 * excerpt the text and leave a dedicated number of chars. 138 * 139 * @param { Number } number the total number of chars should be in element 140 */ 141 _excerptChars(number) { 142 if(number <= 0) 143 return new Error('Number of chars to be shown is equal to or less than zero !!'); 144 145 if(number >= this.text.length) 146 return null; 147 148 this.element.html(this.text.slice(0, number) + '...'); 149 this.element.trigger(EVENTS.excerpted); 150 return true; 151 152 } 153 154 155 156 /** 157 * excerpts the text and leave a dedicated 158 * number of lines. 159 * 160 * @param { Number } linesNo positive number that represent lines no. 161 */ 162 _excerptLines(linesNo) { 163 let lineHeight; 164 let originalHeight = this.element.text(this.text).height(); 165 let targetHeight; 166 let start = 0; 167 let end = this.text.length - 1; 168 let middle; 169 170 lineHeight = this.element.text('w').height(); 171 targetHeight = lineHeight * linesNo; 172 173 if(originalHeight <= targetHeight) { 174 this.element.text(this.text); 175 return false;
176 } 177 178 while(start <= end) { 179 middle = Math.floor((start + end) / 2); 180 this.element.text(this.text.slice(0, middle)); 181 182 if(this.element.height() <= targetHeight) { 183 start = middle + 1; 184 } else { 185 end = middle - 1; 186 } 187 } 188 189 this.element.text(this.text.slice(0, middle - 3) + '...'); 190 this.element.trigger(EVENTS.excerpted); 191 return true; 192 } 193 194 195 196 /** 197 * handler that used to call update method 198 * with a debounce period. 199 */ 200 _updateOnResize() { 201 clearTimeout(this._resizeTimeout); 202 203 this._resizeTimeout = setTimeout(() => { 204 this.update(); 205 }, 300); 206 } 207 208 209 210 /**======================================================================== 211 * GETTERS 212 *========================================================================*/ 213 static get DEFAULTS () { 214 return Object.freeze(DEFAULTS); 215 } 216 217 } 218 219 220 /** 221 * extend JQuery fn and initialize the plugin 222 * @param {Object} options custom setting of Plugin 223 * @return {Object<Plugin>} instance of jquery Plugin 224 */ 225 $.fn[PLUGIN_NAME] = function ( options ) { 226 var args = arguments; 227 228 if (options === undefined || typeof options === 'object') { 229 return this.each(function () { 230 if (!$.data(this, 'plugin_' + PLUGIN_NAME)) { 231 $.data(this, 'plugin_' + PLUGIN_NAME, new Ellipsis( this, options )); 232 } 233 }); 234 } else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') { 235 var returns; 236 237 this.each(function () { 238 var instance = $.data(this, 'plugin_' + PLUGIN_NAME); 239 240 if (instance instanceof Ellipsis && typeof instance[options] === 'function') { 241 returns = instance[options].apply( instance, Array.prototype.slice.call( args, 1 ) ); 242 } 243 244 if (options === 'destroy') { 245 $.data(this, 'plugin_' + PLUGIN_NAME, null); 246 } 247 }); 248 249 return returns !== undefined ? returns : this; 250 } 251 }; 252 253 254 255 // init the plugin on data attribute 256 $(document).ready(function() { 257 let elementsToEllipsis = $('[data-toggle="ellipsis"]'); 258 259 elementsToEllipsis.each((i, element) => { 260 let $element = $(element); 261 let options = {}; 262 263 if( $element.data('type') !== undefined ) 264 options.type = $element.data('type'); 265 266 if( $element.data('count') !== undefined ) 267 options.count = $element.data('count'); 268 269 $element.ellipsis(options); 270 }); 271 }); 272 273 /* strip-code */ 274 window.__ellipsis__ = Ellipsis; 275 /* end-strip-code */ 276 277})(jQuery, window, document);
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.