PageSourceSearch

https://justwebstudio.ru/modules/mod_vater_just/js/jquery.ripples.js

js justwebstudio.ru collected 2026-10-08 12:32:10 UTC 24,249 bytes, 894 lines download raw bytes

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

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.