PageSourceSearch

https://astrapool-rus.ru/local/templates/astraNew/js/jquery.ripples.js?175144507124594

js astrapool-rus.ru collected 2026-10-10 00:08:31 UTC 24,594 bytes, 899 lines download raw bytes

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

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.