1/*! BigText - v0.1.7a - 2014-07-18 2 * https://github.com/zachleat/bigtext 3 * Copyright (c) 2014 Zach Leatherman (@zachleat) 4 * MIT License */ 5 6(function(window, $) { 7 "use strict"; 8 9 var counter = 0, 10 $headCache = $('head'), 11 oldBigText = window.BigText, 12 oldjQueryMethod = $.fn.bigtext, 13 BigText = { 14 DEBUG_MODE: false, 15 DEFAULT_MIN_FONT_SIZE_PX: null, 16 DEFAULT_MAX_FONT_SIZE_PX: 528, 17 GLOBAL_STYLE_ID: 'bigtext-style', 18 STYLE_ID: 'bigtext-id', 19 LINE_CLASS_PREFIX: 'bigtext-line', 20 EXEMPT_CLASS: 'bigtext-exempt', 21 noConflict: function(restore) 22 { 23 if(restore) { 24 $.fn.bigtext = oldjQueryMethod; 25 window.BigText = oldBigText; 26 } 27 return BigText; 28 }, 29 test: { 30 wholeNumberFontSizeOnly: function() { 31 if( !( 'getComputedStyle' in window ) || document.body == null ) { 32 return true; 33 } 34 var test = $('<div/>').css({ 35 position: 'absolute', 36 'font-size': '14.1px' 37 }).appendTo(document.body).get(0), 38 computedStyle = window.getComputedStyle( test, null ); 39 40 if( computedStyle ) { 41 return computedStyle.getPropertyValue( 'font-size' ) === '14px'; 42 } 43 return true; 44 } 45 }, 46 supports: { 47 wholeNumberFontSizeOnly: undefined 48 }, 49 init: function() { 50 if( BigText.supports.wholeNumberFontSizeOnly === undefined ) { 51 BigText.supports.wholeNumberFontSizeOnly = BigText.test.wholeNumberFontSizeOnly(); 52 } 53 54 if(!$('#'+BigText.GLOBAL_STYLE_ID).length) { 55 $headCache.append(BigText.generateStyleTag(BigText.GLOBAL_STYLE_ID, ['.bigtext * { white-space: nowrap; } .bigtext > * { display: block; }', 56 '.bigtext .' + BigText.EXEMPT_CLASS + ', .bigtext .' + BigText.EXEMPT_CLASS + ' * { white-space: normal; }'])); 57 } 58 }, 59 bindResize: function(eventName, resizeFunction) { 60 if($.throttle) { 61 // https://github.com/cowboy/jquery-throttle-debounce 62 $(window).unbind(eventName).bind(eventName, $.throttle(100, resizeFunction)); 63 } else { 64 if($.fn.smartresize) { 65 // https://github.com/lrbabe/jquery-smartresize/ 66 eventName = 'smartresize.' + eventName; 67 } 68 $(window).unbind(eventName).bind(eventName, resizeFunction); 69 } 70 }, 71 getStyleId: function(id) 72 { 73 return BigText.STYLE_ID + '-' + id; 74 }, 75 generateStyleTag: function(id, css) 76 { 77 return $('<style>' + css.join('\n') + '</style>').attr('id', id); 78 }, 79 clearCss: function(id) 80 { 81 var styleId = BigText.getStyleId(id); 82 $('#' + styleId).remove(); 83 }, 84 generateCss: function(id, linesFontSizes, lineWordSpacings, minFontSizes) 85 { 86 var css = []; 87 88 BigText.clearCss(id); 89 90 for(var j=0, k=linesFontSizes.length; j<k; j++) { 91 css.push('#' + id + ' .' + BigText.LINE_CLASS_PREFIX + j + ' {' + 92 (minFontSizes[j] ? ' white-space: normal;' : '') + 93 (linesFontSizes[j] ? ' font-size: ' + linesFontSizes[j] + 'px;' : '') + 94 (lineWordSpacings[j] ? ' word-spacing: ' + lineWordSpacings[j] + 'px;' : '') + 95 '}'); 96 } 97 98 return BigText.generateStyleTag(BigText.getStyleId(id), css); 99 }, 100 jQueryMethod: function(options) 101 { 102 BigText.init(); 103 104 options = $.extend({ 105 minfontsize: BigText.DEFAULT_MIN_FONT_SIZE_PX, 106 maxfontsize: BigText.DEFAULT_MAX_FONT_SIZE_PX, 107 childSelector: '', 108 resize: true 109 }, options || {}); 110 111 this.each(function() 112 { 113 var $t = $(this).addClass('bigtext'), 114 maxWidth = $t.width(), 115 id = $t.attr('id'), 116 $children = options.childSelector ? $t.find( options.childSelector ) : $t.children(); 117 118 if(!id) { 119 id = 'bigtext-id' + (counter++); 120 $t.attr('id', id); 121 } 122 123 if(options.resize) { 124 BigText.bindResize('resize.bigtext-event-' + id, function() 125 { 126 // TODO only call this if the width has changed. 127 BigText.jQueryMethod.call($('#' + id), options); 128 }); 129 } 130 131 BigText.clearCss(id); 132
133 $children.addClass(function(lineNumber, className) 134 { 135 // remove existing line classes. 136 return [className.replace(new RegExp('\\b' + BigText.LINE_CLASS_PREFIX + '\\d+\\b'), ''), 137 BigText.LINE_CLASS_PREFIX + lineNumber].join(' '); 138 }); 139 140 var sizes = calculateSizes($t, $children, maxWidth, options.maxfontsize, options.minfontsize); 141 $headCache.append(BigText.generateCss(id, sizes.fontSizes, sizes.wordSpacings, sizes.minFontSizes)); 142 }); 143 144 return this.trigger('bigtext:complete'); 145 } 146 }; 147 148 function testLineDimensions($line, maxWidth, property, size, interval, units, previousWidth) 149 { 150 var width; 151 previousWidth = typeof previousWidth === 'number' ? previousWidth : 0; 152 $line.css(property, size + units); 153 154 width = $line.width(); 155 156 if(width >= maxWidth) { 157// console.log(width, ' previous: ' + previousWidth, property + ' at ' + interval, 'prior: ' + (parseFloat(size) - interval), 'new:' + parseFloat(size)); 158 $line.css(property, ''); 159 160 if(width === maxWidth) { 161 return { 162 match: 'exact', 163 size: parseFloat((parseFloat(size) - 0.1).toFixed(3)) 164 }; 165 } 166 167 // Since this is an estimate, we calculate how far over the width we went with the new value. 168 // If this is word-spacing (our last resort guess) and the over is less than the under, we keep the higher value. 169 // Otherwise, we revert to the underestimate. 170 var under = maxWidth - previousWidth, 171 over = width - maxWidth; 172 173 return { 174 match: 'estimate', 175 size: parseFloat((parseFloat(size) - (property === 'word-spacing' && previousWidth && ( over < under ) ? 0 : interval)).toFixed(3)) 176 }; 177 } 178 179 return width; 180 } 181 182 function calculateSizes($t, $children, maxWidth, maxFontSize, minFontSize) 183 { 184 var $c = $t.clone(true) 185 .addClass('bigtext-cloned') 186 .css({ 187 fontFamily: $t.css('font-family'), 188 textTransform: $t.css('text-transform'), 189 wordSpacing: $t.css('word-spacing'), 190 letterSpacing: $t.css('letter-spacing'), 191 position: 'absolute', 192 left: BigText.DEBUG_MODE ? 0 : -9999, 193 top: BigText.DEBUG_MODE ? 0 : -9999 194 }) 195 .appendTo(document.body); 196 197 // font-size isn't the only thing we can modify, we can also mess with: 198 // word-spacing and letter-spacing. WebKit does not respect subpixel 199 // letter-spacing, word-spacing, or font-size. 200 // TODO try -webkit-transform: scale() as a workaround. 201 var fontSizes = [], 202 wordSpacings = [], 203 minFontSizes = [], 204 ratios = []; 205 206 $children.css('float', 'left').each(function() { 207 var $line = $(this), 208 // TODO replace 8, 4 with a proportional size to the calculated font-size. 209 intervals = BigText.supports.wholeNumberFontSizeOnly ? [8, 4, 1] : [8, 4, 1, 0.1], 210 lineMax, 211 newFontSize; 212 213 if($line.hasClass(BigText.EXEMPT_CLASS)) { 214 fontSizes.push(null); 215 ratios.push(null); 216 minFontSizes.push(false); 217 return; 218 } 219 220 // TODO we can cache this ratio? 221 var autoGuessSubtraction = 32, // font size in px 222 currentFontSize = parseFloat($line.css('font-size')), 223 ratio = ( $line.width() / currentFontSize ).toFixed(6); 224 225 newFontSize = parseInt( maxWidth / ratio, 10 ) - autoGuessSubtraction; 226 227 outer: for(var m=0, n=intervals.length; m<n; m++) { 228 inner: for(var j=1, k=10; j<=k; j++) { 229 if(newFontSize + j*intervals[m] > maxFontSize) { 230 newFontSize = maxFontSize; 231 break outer; 232 } 233 234 lineMax = testLineDimensions($line, maxWidth, 'font-size', newFontSize + j*intervals[m], intervals[m], 'px', lineMax); 235 if(typeof lineMax !== 'number') { 236 newFontSize = lineMax.size; 237 238 if(lineMax.match === 'exact') { 239 break outer; 240 } 241 break inner; 242 } 243 } 244 } 245 246 ratios.push(maxWidth / newFontSize); 247 248 if(newFontSize > maxFontSize) { 249 fontSizes.push(maxFontSize); 250 minFontSizes.push(false); 251 } else if(!!minFontSize && newFontSize < minFontSize) { 252 fontSizes.push(minFontSize); 253 minFontSizes.push(true); 254 } else { 255 fontSizes.push(newFontSize); 256 minFontSizes.push(false); 257 } 258 }).each(function(lineNumber) { 259 var $line = $(this), 260 wordSpacing = 0, 261 interval = 1, 262 maxWordSpacing; 263 264 if($line.hasClass(BigText.EXEMPT_CLASS)) { 265 wordSpacings.push(null); 266 return; 267 } 268 269 // must re-use font-size, even though it was removed above. 270 $line.css('font-size', fontSizes[lineNumber] + 'px'); 271 272 for(var m=1, n=3; m<n; m+=interval) { 273 maxWordSpacing = testLineDimensions($line, maxWidth, 'word-spacing', m, interval, 'px', maxWordSpacing); 274 if(typeof maxWordSpacing !== 'number') { 275 wordSpacing = maxWordSpacing.size; 276 break; 277 } 278 } 279 280 $line.css('font-size', ''); 281 wordSpacings.push(wordSpacing); 282 }).removeAttr('style'); 283 284 if( !BigText.DEBUG_MODE ) { 285 $c.remove(); 286 } else { 287 $c.css({ 288 'background-color': 'rgba(255,255,255,.4)' 289 }); 290 } 291 292 return { 293 fontSizes: fontSizes, 294 wordSpacings: wordSpacings, 295 ratios: ratios, 296 minFontSizes: minFontSizes 297 }; 298 } 299 300 $.fn.bigtext = BigText.jQueryMethod; 301 window.BigText = BigText; 302 303})(this, 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.