1/* 2 * -- grayscale.js -- 3 * Copyright (C) James Padolsey (http://james.padolsey.com) 4 * 5 */ 6 7var grayscale = (function () { 8 9 var config = { 10 colorProps: [ 11 'color', 'backgroundColor', 'borderBottomColor', 'borderTopColor', 'borderLeftColor', 12 'borderRightColor', 'backgroundImage' 13 ], 14 externalImageHandler: { 15 /* Grayscaling externally hosted images does not work 16 - Use these functions to handle those images as you so desire */ 17 /* Out of convenience these functions are also used for browsers 18 like Chrome that do not support CanvasContext.getImageData */ 19 init: function (el, src) { 20 if (el.nodeName.toLowerCase() === 'img') { 21 // Is IMG element... 22 } else { 23 // Is background-image element: 24 // Default - remove background images 25 data(el).backgroundImageSRC = src; 26 el.style.backgroundImage = ''; 27 } 28 }, 29 reset: function (el) { 30 if (el.nodeName.toLowerCase() === 'img') { 31 // Is IMG element... 32 } else { 33 // Is background-image element: 34 el.style.backgroundImage = 'url(' + (data(el).backgroundImageSRC || '') + ')'; 35 } 36 } 37 } 38 }, 39 log = function () { 40 try { 41 window.console.log.apply(console, arguments); 42 } catch (e) { 43 }; 44 }, 45 isExternal = function (url) { 46 // Checks whether URL is external: 'CanvasContext.getImageData' 47 // only works if the image is on the current domain. 48 return (new RegExp('https?://(?!' + window.location.hostname + ')')).test(url); 49 }, 50 data = (function () { 51 52 var cache = [0], 53 expando = 'data' + (+new Date()); 54 55 return function (elem) { 56 var cacheIndex = elem[expando], 57 nextCacheIndex = cache.length; 58 if (!cacheIndex) { 59 cacheIndex = elem[expando] = nextCacheIndex; 60 cache[cacheIndex] = {}; 61 } 62 return cache[cacheIndex]; 63 }; 64 65 })(), 66 desatIMG = function (img, prepare, realEl) { 67 68 // realEl is only set when img is temp (for BG images) 69 70 var canvas = document.createElement('canvas'), 71 context = canvas.getContext('2d'), 72 height = img.naturalHeight || img.offsetHeight || img.height, 73 width = img.naturalWidth || img.offsetWidth || img.width, 74 imgData; 75 76 canvas.height = height; 77 canvas.width = width; 78 context.drawImage(img, 0, 0); 79 try { 80 imgData = context.getImageData(0, 0, width, height); 81 } catch (e) { 82 } 83 84 if (prepare) { 85 desatIMG.preparing = true; 86 // Slowly recurse through pixels for prep, 87 // :: only occurs on grayscale.prepare() 88 var y = 0; 89 (function () { 90 91 if (!desatIMG.preparing) { 92 return; 93 } 94 95 if (y === height) { 96 // Finished! 97 context.putImageData(imgData, 0, 0, 0, 0, width, height); 98 realEl 99 ? (data(realEl).BGdataURL = canvas.toDataURL()) 100 : (data(img).dataURL = canvas.toDataURL()); 101 } 102 103 for (var x = 0; x < width; x++) { 104 var i = (y * width + x) * 4; 105 // Apply Monoschrome level across all channels: 106 imgData.data[i] = imgData.data[i + 1] = imgData.data[i + 2] = 107 RGBtoGRAYSCALE(imgData.data[i], imgData.data[i + 1], imgData.data[i + 2]); 108 } 109 110 y++; 111 setTimeout(arguments.callee, 0); 112 113 })(); 114 return; 115 } else { 116 // If desatIMG was called without 'prepare' flag 117 // then cancel recursion and proceed with force! (below) 118 desatIMG.preparing = false;
119 } 120 121 for (var y = 0; y < height; y++) { 122 for (var x = 0; x < width; x++) { 123 var i = (y * width + x) * 4; 124 // Apply Monoschrome level across all channels: 125 imgData.data[i] = imgData.data[i + 1] = imgData.data[i + 2] = 126 RGBtoGRAYSCALE(imgData.data[i], imgData.data[i + 1], imgData.data[i + 2]); 127 } 128 } 129 130 context.putImageData(imgData, 0, 0, 0, 0, width, height); 131 return canvas; 132 133 }, 134 getStyle = function (el, prop) { 135 var style = document.defaultView && document.defaultView.getComputedStyle 136 ? document.defaultView.getComputedStyle(el, null)[prop] 137 : el.currentStyle[prop]; 138 // If format is #FFFFFF: (convert to RGB) 139 if (style && /^#[A-F0-9]/i.test(style)) { 140 var hex = style.match(/[A-F0-9]{2}/ig); 141 style = 'rgb(' + parseInt(hex[0], 16) + ',' + parseInt(hex[1], 16) + ',' + parseInt(hex[2], 16) + ')'; 142 } 143 return style; 144 }, 145 RGBtoGRAYSCALE = function (r, g, b) { 146 // Returns single monochrome figure: 147 return parseInt((0.2125 * r) + (0.7154 * g) + (0.0721 * b), 10); 148 }, 149 getAllNodes = function (context) { 150 var all = Array.prototype.slice.call(context.getElementsByTagName('*')); 151 all.unshift(context); 152 return all; 153 }; 154 155 var init = function (context) { 156 157 // Handle if a DOM collection is passed instead of a single el: 158 if (context && context[0] && context.length && context[0].nodeName) { 159 // Is a DOM collection: 160 var allContexts = Array.prototype.slice.call(context), 161 cIndex = -1, 162 cLen = allContexts.length; 163 while (++cIndex < cLen) { 164 init.call(this, allContexts[cIndex]); 165 } 166 return; 167 } 168 169 context = context || document.documentElement; 170 171 if (!document.createElement('canvas').getContext) { 172 context.style.filter = 'progid:DXImageTransform.Microsoft.BasicImage(grayscale=1)'; 173 context.style.zoom = 1; 174 return; 175 } 176 177 var all = getAllNodes(context), 178 i = -1, 179 len = all.length; 180 181 while (++i < len) { 182 var cur = all[i]; 183 184 if (cur.nodeName.toLowerCase() === 'img') { 185 var src = cur.getAttribute('src'); 186 if (!src) { 187 continue; 188 } 189 if (isExternal(src)) { 190 config.externalImageHandler.init(cur, src); 191 } else { 192 data(cur).realSRC = src; 193 try {
194 // Within try statement just encase there's no support.... 195 cur.src = data(cur).dataURL || desatIMG(cur).toDataURL(); 196 } catch (e) { 197 config.externalImageHandler.init(cur, src); 198 } 199 } 200 201 } else { 202 for (var pIndex = 0, 203 pLen = config.colorProps.length; 204 pIndex < pLen; 205 pIndex++) { 206 var prop = config.colorProps[pIndex], 207 style = getStyle(cur, prop); 208 if (!style) { 209 continue; 210 } 211 if (cur.style[prop]) { 212 data(cur)[prop] = style; 213 } 214 // RGB color: 215 if (style.substring(0, 4) === 'rgb(') { 216 var monoRGB = RGBtoGRAYSCALE.apply(null, style.match(/\d+/g)); 217 cur.style[prop] = style = 'rgb(' + monoRGB + ',' + monoRGB + ',' + monoRGB + ')'; 218 continue; 219 } 220 // Background Image:
221 if (style.indexOf('url(') > -1) { 222 var urlPatt = /\(['"]?(.+?)['"]?\)/, 223 url = style.match(urlPatt)[1]; 224 if (isExternal(url)) { 225 config.externalImageHandler.init(cur, url); 226 data(cur).externalBG = true; 227 continue; 228 } 229 // data(cur).BGdataURL refers to caches URL (from preparation) 230 try { 231 var imgSRC = data(cur).BGdataURL || 232 (function () { 233 var temp = document.createElement('img'); 234 temp.src = url; 235 return desatIMG(temp).toDataURL(); 236 })(); 237 238 cur.style[prop] = style.replace(urlPatt, 239 function (_, url) { 240 return '(' + imgSRC + ')'; 241 }); 242 } catch (e) { 243 config.externalImageHandler.init(cur, url); 244 } 245 } 246 } 247 } 248 } 249 250 }; 251 252 init.reset = function (context) { 253 // Handle if a DOM collection is passed instead of a single el: 254 if (context && context[0] && context.length && context[0].nodeName) { 255 // Is a DOM collection: 256 var allContexts = Array.prototype.slice.call(context), 257 cIndex = -1, 258 cLen = allContexts.length; 259 while (++cIndex < cLen) { 260 init.reset.call(this, allContexts[cIndex]); 261 } 262 return; 263 } 264 context = context || document.documentElement; 265 if (!document.createElement('canvas').getContext) { 266 context.style.filter = 'progid:DXImageTransform.Microsoft.BasicImage(grayscale=0)'; 267 return; 268 } 269 var all = getAllNodes(context), 270 i = -1, 271 len = all.length; 272 while (++i < len) { 273 var cur = all[i]; 274 if (cur.nodeName.toLowerCase() === 'img') { 275 var src = cur.getAttribute('src'); 276 if (isExternal(src)) { 277 config.externalImageHandler.reset(cur, src); 278 } 279 cur.src = data(cur).realSRC || src; 280 } else { 281 for (var pIndex = 0, 282 pLen = config.colorProps.length; 283 pIndex < pLen; 284 pIndex++) { 285 if (data(cur).externalBG) { 286 config.externalImageHandler.reset(cur); 287 } 288 var prop = config.colorProps[pIndex]; 289 cur.style[prop] = data(cur)[prop] || ''; 290 } 291 } 292 } 293 }; 294 295 init.prepare = function (context) { 296 297 // Handle if a DOM collection is passed instead of a single el: 298 if (context && context[0] && context.length && context[0].nodeName) { 299 // Is a DOM collection: 300 var allContexts = Array.prototype.slice.call(context), 301 cIndex = -1, 302 cLen = allContexts.length; 303 while (++cIndex < cLen) {
304 init.prepare.call(null, allContexts[cIndex]); 305 } 306 return; 307 } 308 309 // Slowly recurses through all elements 310 // so as not to lock up on the user. 311 312 context = context || document.documentElement; 313 314 if (!document.createElement('canvas').getContext) { 315 return; 316 } 317 318 var all = getAllNodes(context), 319 i = -1, 320 len = all.length; 321 322 while (++i < len) { 323 var cur = all[i]; 324 if (data(cur).skip) { 325 return; 326 } 327 if (cur.nodeName.toLowerCase() === 'img') { 328 if (cur.getAttribute('src') && !isExternal(cur.src)) { 329 desatIMG(cur, true); 330 } 331 332 } else { 333 var style = getStyle(cur, 'backgroundImage'); 334 if (style.indexOf('url(') > -1) { 335 var urlPatt = /\(['"]?(.+?)['"]?\)/, 336 url = style.match(urlPatt)[1]; 337 if (!isExternal(url)) { 338 var temp = document.createElement('img'); 339 temp.src = url; 340 desatIMG(temp, true, cur); 341 } 342 } 343 } 344 } 345 }; 346 347 return init; 348 349})();
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.