vendor: 11,260 bytes, lines 1-341
1/*! 2 * @preserve 3 * 4 * Readmore.js jQuery plugin 5 * Author: @jed_foster 6 * Project home: http://jedfoster.github.io/Readmore.js 7 * Licensed under the MIT license 8 * 9 * Debounce function from http://davidwalsh.name/javascript-debounce-function 10 */ 11 12/* global jQuery */ 13 14(function(factory) { 15 if (typeof define === 'function' && define.amd) { 16 // AMD 17 define(['jquery'], factory); 18 } else if (typeof exports === 'object') { 19 // CommonJS 20 module.exports = factory(require('jquery')); 21 } else { 22 // Browser globals 23 factory(jQuery); 24 } 25}(function($) { 26 'use strict'; 27 28 var readmore = 'readmore', 29 defaults = { 30 speed: 100, 31 collapsedHeight: 200, 32 heightMargin: 16, 33 moreLink: '<a href="#">Read More</a>', 34 lessLink: '<a href="#">Close</a>', 35 embedCSS: true, 36 blockCSS: 'display: block; width: 100%;', 37 startOpen: false, 38 39 // callbacks 40 blockProcessed: function() {}, 41 beforeToggle: function() {}, 42 afterToggle: function() {} 43 }, 44 cssEmbedded = {}, 45 uniqueIdCounter = 0; 46 47 function debounce(func, wait, immediate) { 48 var timeout; 49 50 return function() { 51 var context = this, args = arguments; 52 var later = function() { 53 timeout = null; 54 if (! immediate) { 55 func.apply(context, args); 56 } 57 }; 58 var callNow = immediate && !timeout; 59 60 clearTimeout(timeout); 61 timeout = setTimeout(later, wait); 62 63 if (callNow) { 64 func.apply(context, args); 65 } 66 }; 67 } 68 69 function uniqueId(prefix) { 70 var id = ++uniqueIdCounter; 71 72 return String(prefix == null ? 'rmjs-' : prefix) + id; 73 } 74 75 function setBoxHeights(element) { 76 var el = element.clone().css({ 77 height: 'auto', 78 width: element.width(), 79 maxHeight: 'none', 80 overflow: 'hidden' 81 }).insertAfter(element), 82 expandedHeight = el.outerHeight(), 83 cssMaxHeight = parseInt(el.css({maxHeight: ''}).css('max-height').replace(/[^-\d\.]/g, ''), 10), 84 defaultHeight = element.data('defaultHeight'); 85 86 el.remove(); 87 88 var collapsedHeight = cssMaxHeight || element.data('collapsedHeight') || defaultHeight; 89 90 // Store our measurements. 91 element.data({ 92 expandedHeight: expandedHeight, 93 maxHeight: cssMaxHeight, 94 collapsedHeight: collapsedHeight 95 }) 96 // and disable any `max-height` property set in CSS 97 .css({ 98 maxHeight: 'none' 99 }); 100 } 101 102 var resizeBoxes = debounce(function() { 103 $('[data-readmore]').each(function() { 104 var current = $(this), 105 isExpanded = (current.attr('aria-expanded') === 'true'); 106 107 setBoxHeights(current); 108 109 current.css({ 110 height: current.data( (isExpanded ? 'expandedHeight' : 'collapsedHeight') ) 111 }); 112 }); 113 }, 100); 114 115 function embedCSS(options) { 116 if (! cssEmbedded[options.selector]) { 117 var styles = ' '; 118 119 if (options.embedCSS && options.blockCSS !== '') { 120 styles += options.selector + ' + [data-readmore-toggle], ' + 121 options.selector + '[data-readmore]{' + 122 options.blockCSS + 123 '}'; 124 } 125 126 // Include the transition CSS even if embedCSS is false 127 styles += options.selector + '[data-readmore]{' + 128 'transition: height ' + options.speed + 'ms;' + 129 'overflow: hidden;' + 130 '}'; 131 132 (function(d, u) { 133 var css = d.createElement('style'); 134 css.type = 'text/css'; 135 136 if (css.styleSheet) { 137 css.styleSheet.cssText = u; 138 } 139 else { 140 css.appendChild(d.createTextNode(u)); 141 } 142 143 d.getElementsByTagName('head')[0].appendChild(css); 144 }(document, styles)); 145 146 cssEmbedded[options.selector] = true; 147 } 148 } 149 150 function Readmore(element, options) { 151 this.element = element; 152 153 this.options = $.extend({}, defaults, options); 154 155 embedCSS(this.options); 156 157 this._defaults = defaults; 158 this._name = readmore; 159 160 this.init(); 161 162 // IE8 chokes on `window.addEventListener`, so need to test for support. 163 if (window.addEventListener) { 164 // Need to resize boxes when the page has fully loaded. 165 window.addEventListener('load', resizeBoxes); 166 window.addEventListener('resize', resizeBoxes); 167 } 168 else { 169 window.attachEvent('load', resizeBoxes); 170 window.attachEvent('resize', resizeBoxes); 171 } 172 } 173 174 175 Readmore.prototype = { 176 init: function() { 177 var current = $(this.element); 178 179 current.data({ 180 defaultHeight: this.options.collapsedHeight, 181 heightMargin: this.options.heightMargin 182 }); 183 184 setBoxHeights(current); 185 186 var collapsedHeight = current.data('collapsedHeight'), 187 heightMargin = current.data('heightMargin'); 188 189 if (current.outerHeight(true) <= collapsedHeight + heightMargin) { 190 // The block is shorter than the limit, so there's no need to truncate it. 191 if (this.options.blockProcessed && typeof this.options.blockProcessed === 'function') { 192 this.options.blockProcessed(current, false); 193 } 194 return true; 195 } 196 else { 197 var id = current.attr('id') || uniqueId(), 198 useLink = this.options.startOpen ? this.options.lessLink : this.options.moreLink; 199 200 current.attr({ 201 'data-readmore': '', 202 'aria-expanded': this.options.startOpen, 203 'id': id 204 }); 205 206 current.after($(useLink) 207 .on('click', (function(_this) { 208 return function(event) { 209 _this.toggle(this, current[0], event); 210 }; 211 })(this)) 212 .attr({ 213 'data-readmore-toggle': id, 214 'aria-controls': id 215 })); 216 217 if (! this.options.startOpen) { 218 current.css({ 219 height: collapsedHeight 220 }); 221 } 222 223 if (this.options.blockProcessed && typeof this.options.blockProcessed === 'function') { 224 this.options.blockProcessed(current, true); 225 } 226 } 227 }, 228 229 toggle: function(trigger, element, event) { 230 if (event) { 231 event.preventDefault(); 232 } 233 234 if (! trigger) { 235 trigger = $('[aria-controls="' + this.element.id + '"]')[0]; 236 } 237 238 if (! element) { 239 element = this.element; 240 } 241 242 var $element = $(element), 243 newHeight = '', 244 newLink = '', 245 expanded = false, 246 collapsedHeight = $element.data('collapsedHeight'); 247 248 if ($element.height() <= collapsedHeight) { 249 newHeight = $element.data('expandedHeight') + 'px'; 250 newLink = 'lessLink'; 251 expanded = true; 252 } 253 else { 254 newHeight = collapsedHeight; 255 newLink = 'moreLink'; 256 } 257 258 // Fire beforeToggle callback 259 // Since we determined the new "expanded" state above we're now out of sync 260 // with our true current state, so we need to flip the value of `expanded` 261 if (this.options.beforeToggle && typeof this.options.beforeToggle === 'function') { 262 this.options.beforeToggle(trigger, $element, ! expanded); 263 } 264 265 $element.css({'height': newHeight}); 266 267 // Fire afterToggle callback 268 $element.on('transitionend', (function(_this) { 269 return function() { 270 if (_this.options.afterToggle && typeof _this.options.afterToggle === 'function') { 271 _this.options.afterToggle(trigger, $element, expanded); 272 } 273 274 $(this).attr({ 275 'aria-expanded': expanded 276 }).off('transitionend'); 277 } 278 })(this)); 279 280 $(trigger).replaceWith($(this.options[newLink]) 281 .on('click', (function(_this) { 282 return function(event) { 283 _this.toggle(this, element, event); 284 }; 285 })(this)) 286 .attr({ 287 'data-readmore-toggle': $element.attr('id'), 288 'aria-controls': $element.attr('id') 289 })); 290 }, 291 292 destroy: function() { 293 $(this.element).each(function() { 294 var current = $(this); 295 296 current.attr({ 297 'data-readmore': null, 298 'aria-expanded': null 299 }) 300 .css({ 301 maxHeight: '', 302 height: '' 303 }) 304 .next('[data-readmore-toggle]') 305 .remove(); 306 307 current.removeData(); 308 }); 309 } 310 }; 311 312 313 $.fn.readmore = function(options) { 314 var args = arguments, 315 selector = this.selector; 316 317 options = options || {}; 318 319 if (typeof options === 'object') { 320 return this.each(function() { 321 if ($.data(this, 'plugin_' + readmore)) { 322 var instance = $.data(this, 'plugin_' + readmore); 323 instance.destroy.apply(instance); 324 } 325 326 options.selector = selector; 327 328 $.data(this, 'plugin_' + readmore, new Readmore(this, options)); 329 }); 330 } 331 else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') { 332 return this.each(function () { 333 var instance = $.data(this, 'plugin_' + readmore); 334 if (instance instanceof Readmore && typeof instance[options] === 'function') { 335 instance[options].apply(instance, Array.prototype.slice.call(args, 1)); 336 } 337 }); 338 } 339 }; 340 341}));
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.