1/** 2 * 3 * Version: 0.3.6 4 * Author: Gianluca Guarini 5 * Contact: [email protected] 6 * Website: http://www.gianlucaguarini.com/ 7 * Twitter: @gianlucaguarini 8 * 9 * Copyright (c) Gianluca Guarini 10 * 11 * Permission is hereby granted, free of charge, to any person 12 * obtaining a copy of this software and associated documentation 13 * files (the "Software"), to deal in the Software without 14 * restriction, including without limitation the rights to use, 15 * copy, modify, merge, publish, distribute, sublicense, and/or sell 16 * copies of the Software, and to permit persons to whom the 17 * Software is furnished to do so, subject to the following 18 * conditions: 19 * 20 * The above copyright notice and this permission notice shall be 21 * included in all copies or substantial portions of the Software. 22 * 23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 25 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 27 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 28 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 29 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 30 * OTHER DEALINGS IN THE SOFTWARE. 31 **/ 32(function ($) { 33 $.fn.extend({ 34 BlackAndWhite: function (customOptions) { 35 'use strict'; 36 var $el = this, 37 38 /** 39 * 40 * Options 41 * 42 */ 43 44 options = $.extend({ 45 hoverEffect: true, 46 webworkerPath: false, 47 invertHoverEffect: false, 48 speed: 500, 49 onImageReady: null, 50 intensity: 1 51 }, customOptions), 52 53 // options shorthand 54 hoverEffect = options.hoverEffect, 55 webworkerPath = options.webworkerPath, 56 invertHoverEffect = options.invertHoverEffect, 57 intensity = (typeof options.intensity === 'number' && options.intensity < 1 && options.intensity > 0) ? options.intensity : 1, 58 fadeSpeedIn = $.isPlainObject(options.speed) ? options.speed.fadeIn : options.speed, 59 fadeSpeedOut = $.isPlainObject(options.speed) ? options.speed.fadeOut : options.speed, 60 $window = $(window), 61 62 /** 63 * 64 * Private vars 65 * 66 */ 67 68 _evtNamespace = '.BlackAndWhite', 69 _isIE7 = (document.all && !window.opera && window.XMLHttpRequest) ? true : false, 70 _browserPrefixes = ' -webkit- -moz- -o- -ms- '.split(' '), 71 _cssPrefixString = {}, 72 73 /** 74 * 75 * features detection 76 * 77 */ 78 79 _cssPrefix = function (property) { 80 if (_cssPrefixString[property] || _cssPrefixString[property] === '') { 81 return _cssPrefixString[property] + property; 82 } 83 var e = document.createElement('div'), 84 prefixes = ['', 'Moz', 'Webkit', 'O', 'ms', 'Khtml']; // Various supports... 85 for (var i in prefixes) { 86 if (typeof e.style[prefixes[i] + property] !== 'undefined') { 87 _cssPrefixString[property] = prefixes[i]; 88 return prefixes[i] + property; 89 } 90 } 91 return property.toLowerCase(); 92 }, 93 // https://github.com/Modernizr/Modernizr/blob/master/feature-detects/css-filters.js 94 _cssfilters = (function () { 95 var el = document.createElement('div'); 96 el.style.cssText = _browserPrefixes.join('filter' + ':blur(2px); '); 97 return !!el.style.length && ((document.documentMode === undefined || document.documentMode > 9)); 98 }()), 99 _supportsCanvas = !!document.createElement('canvas').getContext, 100 /* Check if Web Workers are supported */ 101 _supportWebworker = (function () { 102 return (typeof (Worker) !== 'undefined') ? true : false; 103 }()), 104 _cssFilter = _cssPrefix('Filter'), 105 _imagesArray = [], 106 _webWorker = _supportWebworker && webworkerPath ? new Worker(webworkerPath + 'BnWWorker.js') : false, 107 108 /** 109 * 110 * Private methods 111 * 112 */ 113 114 /** 115 * Mouse leave event callback delegated to the the images container 116 * @param { Object } e jquery event object 117 */ 118 _onMouseLeave = function (e) { 119 $(e.currentTarget) 120 .find('.BWfade') 121 .stop(true, true) 122 .animate({ 123 opacity: invertHoverEffect ? 0 : 1 124 }
124, fadeSpeedOut); 125 }, 126 /** 127 * mouseenter event callback delegated to the the images container ($el) 128 * @param { Object } e jquery event object 129 */ 130 _onMouseEnter = function (e) { 131 $(e.currentTarget) 132 .find('.BWfade') 133 .stop(true, true) 134 .animate({ 135 opacity: invertHoverEffect ? 1 : 0 136 }, fadeSpeedOut); 137 }, 138 /** 139 * Callback triggered anytime an image gets loaded and converted 140 * @param { Object } img: DOM image object 141 */ 142 _onImageReady = function (img) { 143 if (typeof options.onImageReady === 'function') { 144 options.onImageReady(img); 145 } 146 }, 147 /** 148 * Initialize the webworker loop 149 * @param { Int } imagesToLoadlength: the amount of images passed to the plugin but not loaded yet 150 */ 151 _initWebworker = function (imagesToLoadlength) { 152 // start the webworker when all the images are ready 153 if (_webWorker && _supportsCanvas && !_cssfilters && !imagesToLoadlength) { 154 // web worker implementation 155 _webWorkerLoop(); 156 } 157 }, 158 /**
159 * Loop all the images converting them by using the a webworker script (this process is unobstrusive and it does not block the page loading) 160 */ 161 _webWorkerLoop = function () { 162 163 if (!_imagesArray.length) { 164 // terminate the worker 165 // the standard way - http://www.w3.org/TR/workers/#dedicated-workers-and-the-worker-interface 166 if (_webWorker.terminate) { 167 _webWorker.terminate(); 168 } 169 // IE 10 specific - http://msdn.microsoft.com/en-us/library/ie/hh673568(v=vs.85).aspx 170 if (_webWorker.close) { 171 _webWorker.close(); 172 } 173 return; 174 } 175 176 // dispatch the image data to the webworker 177 _webWorker.postMessage({ 178 imgData: _imagesArray[0].imageData, 179 intensity: intensity 180 }); 181 182 // anytime a new image gets converted we continue the loop 183 _webWorker.onmessage = function (event) { 184 _imagesArray[0].ctx.putImageData(event.data, 0, 0); 185 _onImageReady(_imagesArray[0].img); 186 _imagesArray.splice(0, 1); 187 _webWorkerLoop(); 188 }; 189 }, 190 /** 191 * Helper function to check whether an image has been completely loaded 192 * @param { Object } img: DOM image object 193 */ 194 _isImageLoaded = function (img) { 195 return img.complete || (typeof img.naturalWidth !== 'undefined' && img.naturalWidth); 196 }, 197 /** 198 * Use the HTML5 canvas to generate a B&W image 199 * @param { Object } img: DOM image object 200 * @param { Object } canvas: canvas element where we are going to draw 201 * @param { Int } width: image width 202 * @param { Int } height: image height 203 */ 204 _generateCanvasImage = function (img, canvas, width, height) { 205 var ctx = canvas.getContext('2d'), 206 currImg = img, 207 i = 0, 208 grey; 209 210 ctx.drawImage(img, 0, 0, width, height); 211 212 var imageData = ctx.getImageData(0, 0, width, height), 213 px = imageData.data, 214 length = px.length; 215 216 // web worker superfast implementation 217 if (_webWorker) { 218 _imagesArray.push({ 219 imageData: imageData, 220 ctx: ctx, 221 img: img 222 }); 223 } else { 224 225 // no webworker slow implementation 226 for (; i < length; i += 4) { 227 var k = px[i] * 0.3 + px[i + 1] * 0.59 + px[i + 2] * 0.11; 228 px[i] = ~~(k * intensity + px[i] * (1 - intensity)); 229 px[i + 1] = ~~(k * intensity + px[i + 1] * (1 - intensity)); 230 px[i + 2] = ~~(k * intensity + px[i + 2] * (1 - intensity)); 231 } 232 233 ctx.putImageData(imageData, 0, 0); 234 235 _onImageReady(img); 236 } 237 }, 238 /** 239 * Print the html element needed to show the B&W image 240 * @param { Array } $img: jQuery array containing the image 241 * @param { Array } $imageWrapper: jQuery array containing the image parent element 242 */ 243 _injectTags = function ($img, $imageWrapper) { 244 245 var img = $img[0], 246 src = img.src, 247 offset = $img.position(), 248 css = { 249 top: offset.top, 250 left: offset.left, 251 position: 'absolute', 252 '-webkit-transform': 'translate3d(0,0,0)', // fix for webkit browsers 253 opacity: invertHoverEffect ? 0 : 1 254 }, 255 $overlay; 256 257 img.crossOrigin = 'anonymous'; 258 259 if (_supportsCanvas && !_cssfilters) { 260 // add the canvas 261 $overlay = $('<canvas width="' + img.naturalWidth + '" height="' + img.naturalHeight + '" class="BWfade"></canvas>'); 262 263 css.width = $img.width(); 264 css.height = $img.height(); 265 _generateCanvasImage(img, $overlay.get(0), img.naturalWidth, img.naturalHeight); 266 267 } else { 268 269 // it's a modern browser but it doesn't support the css filters 270 if (_supportsCanvas) { 271 css[_cssFilter] = 'grayscale(' + intensity * 100 + '%)'; 272 } else { 273 // it's an old IE 274 css.filter = 'progid:DXImageTransform.Microsoft.BasicImage(grayscale=1)'; 275 } 276 // clone the original image using the css filters
277 $overlay = $img.clone().addClass('BWFilter BWfade'); 278 _onImageReady(img); 279 280 } 281 282 $overlay 283 .css(css) 284 .prependTo($imageWrapper); 285 286 // fix opacity on the old browsers 287 if (!$.support.opacity && invertHoverEffect) { 288 $overlay.animate({ 289 opacity: 0 290 }, 0); 291 } 292 }, 293 _resizeCanvases = function () { 294 $el.each(function (index, currImageWrapper) { 295 var img = $(currImageWrapper).find('img'), 296 currWidth = $(img).width(), 297 currHeight = $(img).height(); 298 299 $(this).find('canvas').css({ 300 width: currWidth, 301 height: currHeight 302 }); 303 304 }); 305 }, 306 /** 307 * Init the plugin stuff 308 */ 309 _init = function () { 310 var imagesToLoadlength = $el.find('img').filter(function () { 311 return !$(this).data('_b&w'); 312 }).length; 313 // convert all the images 314 $el.each(function (index, tmpImageWrapper) { 315 var $imageWrapper = $(tmpImageWrapper), 316 $img = $imageWrapper.find('img'); 317 // this image got already converted 318 if ($img.data('_b&w')) { 319 return; 320 } 321 // if this image is not loaded yet 322 if (!_isImageLoaded($img[0])) { 323 $img.on('load', function () { 324 if ($img.data('_b&w_loaded') || !$img[0].complete) { 325 setTimeout(function () { 326 $img.load(); 327 }, 20); 328 return; 329 } 330 _injectTags($img, $imageWrapper); 331 $img.data('_b&w_loaded', true); 332 imagesToLoadlength--; 333 _initWebworker(imagesToLoadlength); 334 }).load(); 335 } else { 336 imagesToLoadlength--; 337 _injectTags($img, $imageWrapper); 338 } 339 $img.data('_b&w', true); 340 }); 341 342 _initWebworker(imagesToLoadlength); 343 344 // binding the hover effect 345 if (hoverEffect) { 346 $el 347 .unbind(_evtNamespace) 348 .on('mouseleave' + _evtNamespace, _onMouseLeave) 349 .on('mouseenter' + _evtNamespace, _onMouseEnter); 350 } 351 if (_supportsCanvas && !_cssfilters) { 352 $window 353 .unbind(_evtNamespace) 354 .on('resize' + _evtNamespace + ' orientationchange' + _evtNamespace, _resizeCanvases); 355 } 356 357 }; 358 359 /** 360 * 361 * Public Api 362 * 363 */ 364 365 var destroy = function () { 366 $el.off(_evtNamespace); 367 $window.off(_evtNamespace); 368 }; 369 370 _init(); 371 372 return { 373 destroy: destroy 374 }; 375 } 376 }); 377}(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.