PageSourceSearch

https://obrixlabs.com/wp-content/themes/aimo/assets/js/libs/ripple.js?ver=all

js obrixlabs.com collected 2026-10-07 00:31:02 UTC 24,801 bytes, 893 lines download raw bytes

1/*!
2 * jQuery Ripples plugin v0.5.3 / https://github.com/sirxemic/jquery.ripples
3 * MIT License
4 * @author sirxemic / https://sirxemic.com/
5 */
6
7(function(factory) {
8	if (typeof define === 'function' && define.amd) {
9		// AMD. Register as an anonymous module.
10		define(['jquery'], factory);
11	}
12	else if (typeof exports === 'object') {
13		// Node/CommonJS
14		factory(require('jquery'));
15	}
16	else {
17		// Browser globals
18		factory(jQuery);
19	}
20}(function($) {
21
22	'use strict';
23
24	var gl;
25	var $window = $(window); // There is only one window, so why not cache the jQuery-wrapped window?
26
27	function isPercentage(str) {
28		return str[str.length - 1] == '%';
29	}
30
31	/**
32	 *  Load a configuration of GL settings which the browser supports.
33	 *  For example:
34	 *  - not all browsers support WebGL
35	 *  - not all browsers support floating point textures
36	 *  - not all browsers support linear filtering for floating point textures
37	 *  - not all browsers support rendering to floating point textures
38	 *  - some browsers *do* support rendering to half-floating point textures instead.
39	 */
40	function loadConfig() {
41		var canvas = document.createElement('canvas');
42		gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
43
44		if (!gl) {
45			// Browser does not support WebGL.
46			return null;
47		}
48
49		// Load extensions
50		var extensions = {};
51		[
52			'OES_texture_float',
53			'OES_texture_half_float',
54			'OES_texture_float_linear',
55			'OES_texture_half_float_linear'
56		].forEach(function(name) {
57			var extension = gl.getExtension(name);
58			if (extension) {
59				extensions[name] = extension;
60			}
61		});
62
63		// If no floating point extensions are supported we can bail out early.
64		if (!extensions.OES_texture_float) {
65			return null;
66		}
67
68		var configs = [];
69
70		function createConfig(type, glType) {
71			var name = 'OES_texture_' + type,
72					nameLinear = name + '_linear',
73					linearSupport = nameLinear in extensions,
74					configExtensions = [name];
75
76			if (linearSupport) {
77				configExtensions.push(nameLinear);
78			}
79
80			return {
81				type: glType,
82				linearSupport: linearSupport,
83				extensions: configExtensions
84			};
85		}
86
87		configs.push(
88			createConfig('float', gl.FLOAT)
89		);
90
91		if (extensions.OES_texture_half_float) {
92			configs.push(
93				createConfig('half_float', extensions.OES_texture_half_float.HALF_FLOAT_OES)
94			);
95		}
96
97		// Setup the texture and framebuffer
98		var texture = gl.createTexture();
99		var framebuffer = gl.createFramebuffer();
100
101		gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
102		gl.bindTexture(gl.TEXTURE_2D, texture);
103		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
104		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
105		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
106		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
107
108		// Check for each supported texture type if rendering to it is supported
109		var config = null;
110
111		for (var i = 0; i < configs.length; i++) {
112			gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 32, 32, 0, gl.RGBA, configs[i].type, null);
113
114			gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
115			if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE) {
116				config = configs[i];
117				break;
118			}
119		}
120
121		return config;
122	}
123
124	function createImageData(width, height) {
125		try {
126			return new ImageData(width, height);
127		}
128		catch (e) {
129			// Fallback for IE
130			var canvas = document.createElement('canvas');
131			return canvas.getContext('2d').createImageData(width, height);
132		}
133	}
134
135	function translateBackgroundPosition(value) {
136		var parts = value.split(' ');
137
138		if (parts.length === 1) {
139			switch (value) {
140				case 'center':
141					return ['50%', '50%'];
142				case 'top':
143					return ['50%', '0'];
144				case 'bottom':
145					return ['50%', '100%'];
146				case 'left':
147					return ['0', '50%'];
148				case 'right':
149					return ['100%', '50%'];
150				default:
151					return [value, '50%'];
152			}
153		}
154		else {
155			return parts.map(function(part) {
156				switch (value) {
157					case 'center':
158						return '50%';
159					case 'top':
160					case 'left':
161						return '0';
162					case 'right':
163					case 'bottom':
164						return '100%';
165					default:
166						return part;
vendor: 2,362 bytes, lines 167-252
167				}
168			});
169		}
170	}
171
172	function createProgram(vertexSource, fragmentSource, uniformValues) {
173		function compileSource(type, source) {
174			var shader = gl.createShader(type);
175			gl.shaderSource(shader, source);
176			gl.compileShader(shader);
177			if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
178				throw new Error('compile error: ' + gl.getShaderInfoLog(shader));
179			}
180			return shader;
181		}
182
183		var program = {};
184
185		program.id = gl.createProgram();
186		gl.attachShader(program.id, compileSource(gl.VERTEX_SHADER, vertexSource));
187		gl.attachShader(program.id, compileSource(gl.FRAGMENT_SHADER, fragmentSource));
188		gl.linkProgram(program.id);
189		if (!gl.getProgramParameter(program.id, gl.LINK_STATUS)) {
190			throw new Error('link error: ' + gl.getProgramInfoLog(program.id));
191		}
192
193		// Fetch the uniform and attribute locations
194		program.uniforms = {};
195		program.locations = {};
196		gl.useProgram(program.id);
197		gl.enableVertexAttribArray(0);
198		var match, name, regex = /uniform (\w+) (\w+)/g, shaderCode = vertexSource + fragmentSource;
199		while ((match = regex.exec(shaderCode)) != null) {
200			name = match[2];
201			program.locations[name] = gl.getUniformLocation(program.id, name);
202		}
203
204		return program;
205	}
206
207	function bindTexture(texture, unit) {
208		gl.activeTexture(gl.TEXTURE0 + (unit || 0));
209		gl.bindTexture(gl.TEXTURE_2D, texture);
210	}
211
212	function extractUrl(value) {
213		var urlMatch = /url\(["']?([^"']*)["']?\)/.exec(value);
214		if (urlMatch == null) {
215			return null;
216		}
217
218		return urlMatch[1];
219	}
220
221	function isDataUri(url) {
222		return url.match(/^data:/);
223	}
224
225	var config = loadConfig();
226	var transparentPixels = createImageData(32, 32);
227
228	// Extend the css
229	$('head').prepend('<style>.jquery-ripples { position: relative; z-index: 0; }</style>');
230
231	// RIPPLES CLASS DEFINITION
232	// =========================
233
234	var Ripples = function (el, options) {
235		var that = this;
236
237		this.$el = $(el);
238
239		// Init properties from options
240		this.interactive = options.interactive;
241		this.resolution = options.resolution;
242		this.textureDelta = new Float32Array([1 / this.resolution, 1 / this.resolution]);
243
244		this.perturbance = options.perturbance;
245		this.dropRadius = options.dropRadius;
246
247		this.crossOrigin = options.crossOrigin;
248		this.imageUrl = options.imageUrl;
249
250		// Init WebGL canvas
251		var canvas = document.createElement('canvas');
252		canvas.width = this.$el.innerWidth();
253		canvas.height = this.$el.innerHeight();
254		this.canvas = canvas;
255		this.$canvas = $(canvas);
256		this.$canvas.css({
257			position: 'absolute',
258			left: 0,
259			top: 0,
260			right: 0,
261			bottom: 0,
262			zIndex: -1
263		});
264
265		this.$el.addClass('jquery-ripples').append(canvas);
266		this.context = gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
267
268		// Load extensions
269		config.extensions.forEach(function(name) {
270			gl.getExtension(name);
271		});
272
273		// Start listening to window resize events
274		$(window).on('resize', function() {
275			var newWidth = that.$el.innerWidth(),
276					newHeight = that.$el.innerHeight();
277
278			if (newWidth != that.canvas.width || newHeight != that.canvas.height) {
279				canvas.width = newWidth;
280				canvas.height = newHeight;
281			}
282		});
283
284		// Init rendertargets for ripple data.
285		this.textures = [];
286		this.framebuffers = [];
287		this.bufferWriteIndex = 0;
288		this.bufferReadIndex = 1;
289
290		for (var i = 0; i < 2; i++) {
291			var texture = gl.createTexture();
292			var framebuffer = gl.createFramebuffer();
293
294			gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
295			framebuffer.width = this.resolution;
296			framebuffer.height = this.resolution;
297
298			gl.bindTexture(gl.TEXTURE_2D, texture);
299			gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, config.linearSupport ? gl.LINEAR : gl.NEAREST);
300			gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, config.linearSupport ? gl.LINEAR : gl.NEAREST);
301			gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
302			gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
303			gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.resolution, this.resolution, 0, gl.RGBA, config.type, null);
304
305			gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
306
307			this.textures.push(texture);
308			this.framebuffers.push(framebuffer);
309		}
310
311		// Init GL stuff
312		this.quad = gl.createBuffer();
313		gl.bindBuffer(gl.ARRAY_BUFFER, this.quad);
314		gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
315			-1, -1,
316			+1, -1,
317			+1, +1,
318			-1, +1
319		]), gl.STATIC_DRAW);
320
321		this.initShaders();
322		this.initTexture();
323		this.setTransparentTexture();
324
325		// Load the image either from the options or CSS rules
326		this.loadImage();
327
328		// Set correct clear color and blend mode (regular alpha blending)
329		gl.clearColor(0, 0, 0, 0);
330		gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
331
332		// Plugin is successfully initialized!
333		this.visible = true;
334		this.running = true;
335		this.inited = true;
336
337		this.setupPointerEvents();
338
339		// Init animation
340		function step() {
341			that.step();
342			requestAnimationFrame(step);
343		}
344
345		requestAnimationFrame(step);
346	};
347
348	Ripples.DEFAULTS = {
349		imageUrl: null,
350		resolution: 256,
351		dropRadius: 20,
352		perturbance: 0.03,
353		interactive: true,
354		crossOrigin: ''
355	};
356
357	Ripples.prototype = {
358
359		// Set up pointer (mouse + touch) events
360		setupPointerEvents: function() {
361			var that = this;
362
363			function pointerEventsEnabled() {
364				return that.visible && that.running && that.interactive;
365			}
366
367			function dropAtPointer(pointer, big) {
368				if (pointerEventsEnabled()) {
369					that.dropAtPointer(
370						pointer,
371						that.dropRadius * (big ? 1.5 : 1),
372						(big ? 0.14 : 0.01)
373					);
374				}
375			}
376
377			// Start listening to pointer events
378			this.$el
379
380				// Create regular, small ripples for mouse move and touch events...
381				.on('mousemove.ripples', function(e) {
382					dropAtPointer(e);
383				})
384				.on('touchmove.ripples, touchstart.ripples', function(e) {
385					var touches = e.originalEvent.changedTouches;
386					for (var i = 0; i < touches.length; i++) {
387						dropAtPointer(touches[i]);
388					}
389				})
390
391				// ...and only a big ripple on mouse down events.
392				.on('mousedown.ripples', function(e) {
393					dropAtPointer(e, true);
394				});
395		},
396
397		// Load the image either from the options or the element's CSS rules.
398		loadImage: function() {
399			var that = this;
400
401			gl = this.context;
402
403			var newImageSource = this.imageUrl ||
404				extractUrl(this.originalCssBackgroundImage) ||
405				extractUrl(this.$el.css('backgroundImage'));
406
407			// If image source is unchanged, don't reload it.
408			if (newImageSource == this.imageSource) {
409				return;
410			}
411
412			this.imageSource = newImageSource;
413
414			// Falsy source means no background.
415			if (!this.imageSource) {
416				this.setTransparentTexture();
417				return;
418			}
419
420			// Load the texture from a new image.
421			var image = new Image;
422			image.onload = function() {
423				gl = that.context;
424
425				// Only textures with dimensions of powers of two can have repeat wrapping.
426				function isPowerOfTwo(x) {
427					return (x & (x - 1)) == 0;
428				}
429
430				var wrapping = (isPowerOfTwo(image.width) && isPowerOfTwo(image.height)) ? gl.REPEAT : gl.CLAMP_TO_EDGE;
431
432				gl.bindTexture(gl.TEXTURE_2D, that.backgroundTexture);
433				gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapping);
434				gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapping);
435				gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
436
437				that.backgroundWidth = image.width;
438				that.backgroundHeight = image.height;
439
440				// Hide the background that we're replacing.
vendor: 13,113 bytes, lines 441-893
441				that.hideCssBackground();
442			};
443
444			// Fall back to a transparent texture when loading the image failed.
445			image.onerror = function() {
446				gl = that.context;
447
448				that.setTransparentTexture();
449			};
450
451			// Disable CORS when the image source is a data URI.
452			image.crossOrigin = isDataUri(this.imageSource) ? null : this.crossOrigin;
453
454			image.src = this.imageSource;
455		},
456
457		step: function() {
458			gl = this.context;
459
460			if (!this.visible) {
461				return;
462			}
463
464			this.computeTextureBoundaries();
465
466			if (this.running) {
467				this.update();
468			}
469
470			this.render();
471		},
472
473		drawQuad: function() {
474			gl.bindBuffer(gl.ARRAY_BUFFER, this.quad);
475			gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
476			gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);
477		},
478
479		render: function() {
480			gl.bindFramebuffer(gl.FRAMEBUFFER, null);
481
482			gl.viewport(0, 0, this.canvas.width, this.canvas.height);
483
484			gl.enable(gl.BLEND);
485			gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
486
487			gl.useProgram(this.renderProgram.id);
488
489			bindTexture(this.backgroundTexture, 0);
490			bindTexture(this.textures[0], 1);
491
492			gl.uniform1f(this.renderProgram.locations.perturbance, this.perturbance);
493			gl.uniform2fv(this.renderProgram.locations.topLeft, this.renderProgram.uniforms.topLeft);
494			gl.uniform2fv(this.renderProgram.locations.bottomRight, this.renderProgram.uniforms.bottomRight);
495			gl.uniform2fv(this.renderProgram.locations.containerRatio, this.renderProgram.uniforms.containerRatio);
496			gl.uniform1i(this.renderProgram.locations.samplerBackground, 0);
497			gl.uniform1i(this.renderProgram.locations.samplerRipples, 1);
498
499			this.drawQuad();
500			gl.disable(gl.BLEND);
501		},
502
503		update: function() {
504			gl.viewport(0, 0, this.resolution, this.resolution);
505
506			gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffers[this.bufferWriteIndex]);
507			bindTexture(this.textures[this.bufferReadIndex]);
508			gl.useProgram(this.updateProgram.id);
509
510			this.drawQuad();
511
512			this.swapBufferIndices();
513		},
514
515		swapBufferIndices: function() {
516			this.bufferWriteIndex = 1 - this.bufferWriteIndex;
517			this.bufferReadIndex = 1 - this.bufferReadIndex;
518		},
519
520		computeTextureBoundaries: function() {
521			var backgroundSize = this.$el.css('background-size');
522			var backgroundAttachment = this.$el.css('background-attachment');
523			var backgroundPosition = translateBackgroundPosition(this.$el.css('background-position'));
524
525			// Here the 'container' is the element which the background adapts to
526			// (either the chrome window or some element, depending on attachment)
527			var container;
528			if (backgroundAttachment == 'fixed') {
529				container = { left: window.pageXOffset, top: window.pageYOffset };
530				container.width = $window.width();
531				container.height = $window.height();
532			}
533			else {
534				container = this.$el.offset();
535				container.width = this.$el.innerWidth();
536				container.height = this.$el.innerHeight();
537			}
538
539			// TODO: background-clip
540			if (backgroundSize == 'cover') {
541				var scale = Math.max(container.width / this.backgroundWidth, container.height / this.backgroundHeight);
542
543				var backgroundWidth = this.backgroundWidth * scale;
544				var backgroundHeight = this.backgroundHeight * scale;
545			}
546			else if (backgroundSize == 'contain') {
547				var scale = Math.min(container.width / this.backgroundWidth, container.height / this.backgroundHeight);
548
549				var backgroundWidth = this.backgroundWidth * scale;
550				var backgroundHeight = this.backgroundHeight * scale;
551			}
552			else {
553				backgroundSize = backgroundSize.split(' ');
554				var backgroundWidth = backgroundSize[0] || '';
555				var backgroundHeight = backgroundSize[1] || backgroundWidth;
556
557				if (isPercentage(backgroundWidth)) {
558					backgroundWidth = container.width * parseFloat(backgroundWidth) / 100;
559				}
560				else if (backgroundWidth != 'auto') {
561					backgroundWidth = parseFloat(backgroundWidth);
562				}
563
564				if (isPercentage(backgroundHeight)) {
565					backgroundHeight = container.height * parseFloat(backgroundHeight) / 100;
566				}
567				else if (backgroundHeight != 'auto') {
568					backgroundHeight = parseFloat(backgroundHeight);
569				}
570
571				if (backgroundWidth == 'auto' && backgroundHeight == 'auto') {
572					backgroundWidth = this.backgroundWidth;
573					backgroundHeight = this.backgroundHeight;
574				}
575				else {
576					if (backgroundWidth == 'auto') {
577						backgroundWidth = this.backgroundWidth * (backgroundHeight / this.backgroundHeight);
578					}
579
580					if (backgroundHeight == 'auto') {
581						backgroundHeight = this.backgroundHeight * (backgroundWidth / this.backgroundWidth);
582					}
583				}
584			}
585
586			// Compute backgroundX and backgroundY in page coordinates
587			var backgroundX = backgroundPosition[0];
588			var backgroundY = backgroundPosition[1];
589
590			if (isPercentage(backgroundX)) {
591				backgroundX = container.left + (container.width - backgroundWidth) * parseFloat(backgroundX) / 100;
592			}
593			else {
594				backgroundX = container.left + parseFloat(backgroundX);
595			}
596
597			if (isPercentage(backgroundY)) {
598				backgroundY = container.top + (container.height - backgroundHeight) * parseFloat(backgroundY) / 100;
599			}
600			else {
601				backgroundY = container.top + parseFloat(backgroundY);
602			}
603
604			var elementOffset = this.$el.offset();
605
606			this.renderProgram.uniforms.topLeft = new Float32Array([
607				(elementOffset.left - backgroundX) / backgroundWidth,
608				(elementOffset.top - backgroundY) / backgroundHeight
609			]);
610			this.renderProgram.uniforms.bottomRight = new Float32Array([
611				this.renderProgram.uniforms.topLeft[0] + this.$el.innerWidth() / backgroundWidth,
612				this.renderProgram.uniforms.topLeft[1] + this.$el.innerHeight() / backgroundHeight
613			]);
614
615			var maxSide = Math.max(this.canvas.width, this.canvas.height);
616
617			this.renderProgram.uniforms.containerRatio = new Float32Array([
618				this.canvas.width / maxSide,
619				this.canvas.height / maxSide
620			]);
621		},
622
623		initShaders: function() {
624			var vertexShader = [
625				'attribute vec2 vertex;',
626				'varying vec2 coord;',
627				'void main() {',
628					'coord = vertex * 0.5 + 0.5;',
629					'gl_Position = vec4(vertex, 0.0, 1.0);',
630				'}'
631			].join('\n');
632
633			this.dropProgram = createProgram(vertexShader, [
634				'precision highp float;',
635
636				'const float PI = 3.141592653589793;',
637				'uniform sampler2D texture;',
638				'uniform vec2 center;',
639				'uniform float radius;',
640				'uniform float strength;',
641
642				'varying vec2 coord;',
643
644				'void main() {',
645					'vec4 info = texture2D(texture, coord);',
646
647					'float drop = max(0.0, 1.0 - length(center * 0.5 + 0.5 - coord) / radius);',
648					'drop = 0.5 - cos(drop * PI) * 0.5;',
649
650					'info.r += drop * strength;',
651
652					'gl_FragColor = info;',
653				'}'
654			].join('\n'));
655
656			this.updateProgram = createProgram(vertexShader, [
657				'precision highp float;',
658
659				'uniform sampler2D texture;',
660				'uniform vec2 delta;',
661
662				'varying vec2 coord;',
663
664				'void main() {',
665					'vec4 info = texture2D(texture, coord);',
666
667					'vec2 dx = vec2(delta.x, 0.0);',
668					'vec2 dy = vec2(0.0, delta.y);',
669
670					'float average = (',
671						'texture2D(texture, coord - dx).r +',
672						'texture2D(texture, coord - dy).r +',
673						'texture2D(texture, coord + dx).r +',
674						'texture2D(texture, coord + dy).r',
675					') * 0.25;',
676
677					'info.g += (average - info.r) * 2.0;',
678					'info.g *= 0.995;',
679					'info.r += info.g;',
680
681					'gl_FragColor = info;',
682				'}'
683			].join('\n'));
684			gl.uniform2fv(this.updateProgram.locations.delta, this.textureDelta);
685
686			this.renderProgram = createProgram([
687				'precision highp float;',
688
689				'attribute vec2 vertex;',
690				'uniform vec2 topLeft;',
691				'uniform vec2 bottomRight;',
692				'uniform vec2 containerRatio;',
693				'varying vec2 ripplesCoord;',
694				'varying vec2 backgroundCoord;',
695				'void main() {',
696					'backgroundCoord = mix(topLeft, bottomRight, vertex * 0.5 + 0.5);',
697					'backgroundCoord.y = 1.0 - backgroundCoord.y;',
698					'ripplesCoord = vec2(vertex.x, -vertex.y) * containerRatio * 0.5 + 0.5;',
699					'gl_Position = vec4(vertex.x, -vertex.y, 0.0, 1.0);',
700				'}'
701			].join('\n'), [
702				'precision highp float;',
703
704				'uniform sampler2D samplerBackground;',
705				'uniform sampler2D samplerRipples;',
706				'uniform vec2 delta;',
707
708				'uniform float perturbance;',
709				'varying vec2 ripplesCoord;',
710				'varying vec2 backgroundCoord;',
711
712				'void main() {',
713					'float height = texture2D(samplerRipples, ripplesCoord).r;',
714					'float heightX = texture2D(samplerRipples, vec2(ripplesCoord.x + delta.x, ripplesCoord.y)).r;',
715					'float heightY = texture2D(samplerRipples, vec2(ripplesCoord.x, ripplesCoord.y + delta.y)).r;',
716					'vec3 dx = vec3(delta.x, heightX - height, 0.0);',
717					'vec3 dy = vec3(0.0, heightY - height, delta.y);',
718					'vec2 offset = -normalize(cross(dy, dx)).xz;',
719					'float specular = pow(max(0.0, dot(offset, normalize(vec2(-0.6, 1.0)))), 4.0);',
720					'gl_FragColor = texture2D(samplerBackground, backgroundCoord + offset * perturbance) + specular;',
721				'}'
722			].join('\n'));
723			gl.uniform2fv(this.renderProgram.locations.delta, this.textureDelta);
724		},
725
726		initTexture: function() {
727			this.backgroundTexture = gl.createTexture();
728			gl.bindTexture(gl.TEXTURE_2D, this.backgroundTexture);
729			gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
730			gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
731			gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
732		},
733
734		setTransparentTexture: function() {
735			gl.bindTexture(gl.TEXTURE_2D, this.backgroundTexture);
736			gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, transparentPixels);
737		},
738
739		hideCssBackground: function() {
740
741			// Check whether we're changing inline CSS or overriding a global CSS rule.
742			var inlineCss = this.$el[0].style.backgroundImage;
743
744			if (inlineCss == 'none') {
745				return;
746			}
747
748			this.originalInlineCss = inlineCss;
749
750			this.originalCssBackgroundImage = this.$el.css('backgroundImage');
751			this.$el.css('backgroundImage', 'none');
752		},
753
754		restoreCssBackground: function() {
755
756			// Restore background by either changing the inline CSS rule to what it was, or
757			// simply remove the inline CSS rule if it never was inlined.
758			this.$el.css('backgroundImage', this.originalInlineCss || '');
759		},
760
761		dropAtPointer: function(pointer, radius, strength) {
762			var borderLeft = parseInt(this.$el.css('border-left-width')) || 0,
763					borderTop = parseInt(this.$el.css('border-top-width')) || 0;
764
765			this.drop(
766				pointer.pageX - this.$el.offset().left - borderLeft,
767				pointer.pageY - this.$el.offset().top - borderTop,
768				radius,
769				strength
770			);
771		},
772
773		/**
774		 *  Public methods
775		 */
776		drop: function(x, y, radius, strength) {
777			gl = this.context;
778
779			var elWidth = this.$el.innerWidth();
780			var elHeight = this.$el.innerHeight();
781			var longestSide = Math.max(elWidth, elHeight);
782
783			radius = radius / longestSide;
784
785			var dropPosition = new Float32Array([
786				(2 * x - elWidth) / longestSide,
787				(elHeight - 2 * y) / longestSide
788			]);
789
790			gl.viewport(0, 0, this.resolution, this.resolution);
791
792			gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffers[this.bufferWriteIndex]);
793			bindTexture(this.textures[this.bufferReadIndex]);
794
795			gl.useProgram(this.dropProgram.id);
796			gl.uniform2fv(this.dropProgram.locations.center, dropPosition);
797			gl.uniform1f(this.dropProgram.locations.radius, radius);
798			gl.uniform1f(this.dropProgram.locations.strength, strength);
799
800			this.drawQuad();
801
802			this.swapBufferIndices();
803		},
804
805		destroy: function() {
806			this.$el
807				.off('.ripples')
808				.removeClass('jquery-ripples')
809				.removeData('ripples');
810
811			this.$canvas.remove();
812			this.restoreCssBackground();
813		},
814
815		show: function() {
816			this.visible = true;
817
818			this.$canvas.show();
819			this.hideCssBackground();
820		},
821
822		hide: function() {
823			this.visible = false;
824
825			this.$canvas.hide();
826			this.restoreCssBackground();
827		},
828
829		pause: function() {
830			this.running = false;
831		},
832
833		play: function() {
834			this.running = true;
835		},
836
837		set: function(property, value) {
838			switch (property) {
839				case 'dropRadius':
840				case 'perturbance':
841				case 'interactive':
842				case 'crossOrigin':
843					this[property] = value;
844					break;
845				case 'imageUrl':
846					this.imageUrl = value;
847					this.loadImage();
848					break;
849			}
850		}
851	};
852
853	// RIPPLES PLUGIN DEFINITION
854	// ==========================
855
856	var old = $.fn.ripples;
857
858	$.fn.ripples = function(option) {
859		if (!config) {
860			throw new Error('Your browser does not support WebGL, the OES_texture_float extension or rendering to floating point textures.');
861		}
862
863		var args = (arguments.length > 1) ? Array.prototype.slice.call(arguments, 1) : undefined;
864
865		return this.each(function() {
866			var $this = $(this),
867					data = $this.data('ripples'),
868					options = $.extend({}, Ripples.DEFAULTS, $this.data(), typeof option == 'object' && option);
869
870			if (!data && typeof option == 'string') {
871				return;
872			}
873			if (!data) {
874				$this.data('ripples', (data = new Ripples(this, options)));
875			}
876			else if (typeof option == 'string') {
877				Ripples.prototype[option].apply(data, args);
878			}
879		});
880	};
881
882	$.fn.ripples.Constructor = Ripples;
883
884
885	// RIPPLES NO CONFLICT
886	// ====================
887
888	$.fn.ripples.noConflict = function() {
889		$.fn.ripples = old;
890		return this;
891	};
892
893}));

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.