1/* http://keith-wood.name/more.html 2 Text truncation and show more for jQuery v2.0.1. 3 Written by Keith Wood (kwood{at}iinet.com.au) May 2010. 4 Available under the MIT (https://github.com/jquery/jquery/blob/master/LICENSE.txt) license. 5 Please attribute the author if you use it. */ 6 7(function ($) { // Hide scope, no $ conflict 8 9 var pluginName = 'more'; 10 11 /** Create the more plugin. 12 <p>Sets an element to hide/show additional text.</p> 13 <p>Expects HTML like:</p> 14 <pre><p>...</p></pre> 15 <p>Provide inline configuration like:</p> 16 <pre><p data-more="name: 'value'">...</p></pre> 17 @module More 18 @augments JQPlugin 19 @example $(selector).more() 20 $(selector).more({length: 200, toggle: false}) */ 21 $.JQPlugin.createPlugin({ 22 23 /** The name of the plugin. */ 24 name: pluginName, 25 26 /** More/less change callback. 27 Triggered when the more/less button is clicked. 28 @callback changeCallback 29 @param expanding {boolean} True if expanding the text, false if collapsing. */ 30 31 /** Default settings for the plugin. 32 @property [length=100] {number} Truncation length. 33 @property [leeway=5] {number} Overrun allowed without truncation. 34 @property [wordBreak=false] {boolean} True to break between words, false to break anywhere. 35 @property [ignoreTags=['br','hr','img']] {string[]} Non-terminated tags to ignore. 36 @property [toggle=true] {boolean} True to toggle, false to display and exit. 37 @property [ellipsisText='...'] {string} Display text for ellipsis. 38 @property [moreText='Show more'] {string} Display text for more link. 39 @property [lessText='Show less'] {string} Display text for less link. 40 @property [andNext=''] {string} Selector for any following tags to include in the collapse. 41 @property [onChange=null] {changeCallback} Callback function when expanded/collapsed. 42 @example onChange: function(expanding) { 43 console.log('Showing ' + (expanding ? 'more' : 'less')); 44 } */ 45 defaultOptions: { 46 length: 100, 47 leeway: 5, 48 wordBreak: false, 49 ignoreTags: ['br', 'hr', 'img'], 50 toggle: true, 51 ellipsisText: '...', 52 moreText: 'Show more', 53 lessText: 'Show less', 54 andNext: '', 55 onChange: null 56 }, 57 58 _ellipsisClass: pluginName + '-ellipsis', // The ellipsis marker class 59 _linkClass: pluginName + '-link', // The link marker class 60 _hiddenClass: pluginName + '-hidden', // The text hidden marker class 61 62 _tagName: /^<(\w+).*>$/, // Extract a tag name 63 64 _instSettings: function (elem, options) { 65 return { 66 html: elem.html() 67 }; 68 }, 69 70 _optionsChanged: function (elem, inst, options) { 71 var self = this; 72 $.extend(inst.options, options); 73 this._preDestroy(elem, inst); // Reset 74 var html = ''; 75 if (elem.text().length > inst.options.length + inst.options.leeway) { // If text is longer 76 var matches = elem.html().match(/(<[^>]+>)|([^<]+)/g); // Extract text and tags 77 var i = 0; 78 var pos = 0; 79 var tags = []; 80 for (; i < matches.length; i++) { 81 if (matches[i][0] === '<') { // A tag 82 if (matches[i][1] === '/' || matches[i][matches[i].length - 2] === '/') { 83 tags.pop(); // Closing 84 } else if ($.inArray(matches[i].toLowerCase().replace(this._tagName, '$1'), 85 inst.options.ignoreTags) === -1) { 86 tags.push(matches[i]); // Opening 87 } 88 } else if (pos + matches[i].length > inst.options.length) { // Found truncation point 89 break; 90 } else { // Count text 91 pos += matches[i].length; 92 } 93 } 94 pos = inst.options.length - pos; // Split point 95 if (inst.options.wordBreak) { // Move back to start of word 96 var matched = matches[i].substring(0, pos + 1).replace('\n', ' ').match(/^.*\W/m); 97 pos = (matched ? matched[0].length - 1 : pos); 98 } 99 100 var closeTags = function (tags) { // Close any opened tags 101 var html = ''; 102 for (var i = tags.length - 1; i >= 0; i--) { 103 html += '</' + tags[i].replace(self._tagName, '$1') + '>'; 104 } 105 return html; 106 }; 107 108 var closeTagsContainsEllipsisText = function (tags, referi, pos, matches) { // Close any opened tags 109 var html = ''; 110 var firtTime = true; 111 112 for (var i = tags.length - 1; i >= 0; i--) { 113 var tag = tags[i]; 114 var tagEnd = '</' + tag.replace(self._tagName, '$1') + '>' 115 if (firtTime) { 116 html += '<span class="more-ellipsis">' + inst.options.ellipsisText + '</span>'; 117 var moreAddedTags = matches[referi].substring(pos); 118 while (matches[referi + 1] !== tagEnd){ 119 moreAddedTags += matches[referi + 1]; 120 referi++; 121 } 122 123 html += '<span class="more-added-tags">' + moreAddedTags + '</span>'; 124 } 125 126 html += tagEnd; 127 128 if (firtTime) { 129 // rest of text 130 html += '<div class="more-hidden">' 131 html += matches.slice(referi + 1).join(''); 132 html += '</div>'; 133 firtTime = false;
134 } 135 } 136 137 return html; 138 }; 139 140 if (tags.length) { 141 // Generate new content 142 html = matches.slice(0, i).join('') + matches[i].substring(0, pos) + closeTagsContainsEllipsisText(tags, i, pos, matches); 143 } else { 144 // Generate new content 145 html = matches.slice(0, i).join('') + matches[i].substring(0, pos) + closeTags(tags) + 146 '<span class="' + this._ellipsisClass + '">' + inst.options.ellipsisText + '</span>' + 147 '<span class="' + this._hiddenClass + '">' + tags.join('') + 148 matches[i].substring(pos) + closeTags(tags) + matches.slice(i + 1).join('') + '</span>'; 149 } 150 } else if (options.andNext && elem.nextAll(options.andNext).length) { 151 html = elem.html(); 152 } 153 if (html) { 154 html += '<a href="#" class="' + this._linkClass + '">' + inst.options.moreText + '</a>'; 155 elem.html(html).find('a.' + this._linkClass).click(function (event) { 156 var link = $(this); 157 var expanding = link.html() === inst.options.moreText; 158 var expandingContent = link.html(expanding ? inst.options.lessText : inst.options.moreText); 159 var $isMore = expandingContent.closest('.is-more'); 160 161 //toggle content hidden 162 $isMore.find('span.' + self._ellipsisClass + ',span.more-added-tags').toggle(); 163 $isMore.find('.' + self._hiddenClass).toggle(); 164 165 if (options.andNext) { 166 link.parent().nextAll(options.andNext).toggle(); 167 } 168 if (!inst.options.toggle) { // Once only 169 link.remove(); 170 } 171 if ($.isFunction(inst.options.onChange)) { // Callback 172 inst.options.onChange.apply(elem, [expanding]); 173 } 174 event.stopPropagation(); 175 return false; 176 }); 177 178 $('.is-more > *').each(function () { // For each element 179 if (inst.options.ignoreTags.indexOf(this.tagName.toLowerCase()) === -1 180 && $(this).text().trim() === '') 181 $(this).remove(); // if it is empty, it removes it 182 }); 183 184 if (options.andNext) { 185 elem.nextAll(options.andNext).hide(); 186 } 187 } 188 }, 189 190 _preDestroy: function (elem, inst) { 191 elem.html(inst.html); 192 if (inst.options.andNext) { 193 elem.nextAll(inst.options.andNext).show(); 194 } 195 } 196 }); 197 198})(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.