Rainday.min.js Compatibility Issue

    /**
 * Defines a new instance of the rainyday.js.
 * @param options options element with script parameters
 * @param canvas to be used (if not defined a new one will be created)
 */

function RainyDay(options, canvas) {

if (this === window) { //if *this* is the window object, start over with a *new* object
return new RainyDay(options);
}

this.img = options.image;
var defaults = {
opacity: 1,
blur: 10,
crop: [0, 0, this.img.naturalWidth, this.img.naturalHeight],
enableSizeChange: true,
parentElement: document.getElementsByTagName('body')[0],
fps: 30,
fillStyle: '#8ED6FF',
enableCollisions: true,
gravityThreshold: 3,
gravityAngle: Math.PI / 2,
gravityAngleVariance: 0,
reflectionScaledownFactor: 5,
reflectionDropMappingWidth: 200,
reflectionDropMappingHeight: 200,
width: this.img.clientWidth,
height: this.img.clientHeight,
position: 'absolute',
top: 0,
left: 0
};

// add the defaults to options
for (var option in defaults) {
if (typeof options[option] === 'undefined') {
options[option] = defaults[option];
}
}
this.options = options;

this.drops = [];

// prepare canvas elements
this.canvas = canvas || this.prepareCanvas();
this.prepareBackground(this.canvas.width, this.canvas.height);
this.prepareGlass();

// assume defaults
this.reflection = this.REFLECTION_MINIATURE;
this.trail = this.TRAIL_DROPS;
this.gravity = this.GRAVITY_NON_LINEAR;
this.collision = this.COLLISION_SIMPLE;

// set polyfill of requestAnimationFrame
this.setRequestAnimFrame();
}

/**
 * Create the main canvas over a given element
 * @returns HTMLElement the canvas
 */
RainyDay.prototype.prepareCanvas = function() {
var canvas = document.createElement('canvas');
canvas.style.position = this.options.position;
canvas.style.top = this.options.top;
canvas.style.left = this.options.left;
canvas.width = this.options.width;
canvas.height = this.options.height;
this.options.parentElement.appendChild(canvas);
if (this.enableSizeChange) {
this.setResizeHandler();
}
return canvas;
};

RainyDay.prototype.setResizeHandler = function() {
// use setInterval if oneresize event already use by other.
if (window.onresize !== null) {
window.setInterval(this.checkSize.bind(this), 100);
} else {
window.onresize = this.checkSize.bind(this);
window.onorientationchange = this.checkSize.bind(this);
}
};

/**
 * Periodically check the size of the underlying element
 */
RainyDay.prototype.checkSize = function() {
var clientWidth = this.img.clientWidth;
var clientHeight = this.img.clientHeight;
var clientOffsetLeft = this.img.offsetLeft;
var clientOffsetTop = this.img.offsetTop;
var canvasWidth = this.canvas.width;
var canvasHeight = this.canvas.height;
var canvasOffsetLeft = this.canvas.offsetLeft;
var canvasOffsetTop = this.canvas.offsetTop;

if (canvasWidth !== clientWidth || canvasHeight !== clientHeight) {
this.canvas.width = this.canvas.width;
this.canvas.height = this.canvas.height;
this.prepareBackground(this.canvas.width, this.canvas.height);
this.glass.width = this.canvas.width;
this.glass.height = this.canvas.height;
this.prepareReflections();
}
if (canvasOffsetLeft !== clientOffsetLeft || canvasOffsetTop !== clientOffsetTop) {
this.canvas.offsetLeft = clientOffsetLeft;
this.canvas.offsetTop = clientOffsetTop;
}
};

/**
 * Start animation loop
 */
RainyDay.prototype.animateDrops = function() {
if (this.addDropCallback) {
this.addDropCallback();
}
// |this.drops| array may be changed as we iterate over drops
var dropsClone = this.drops.slice();
var newDrops = [];
for (var i = 0; i < dropsClone.length; ++i) {
if (dropsClone[i].animate()) {
newDrops.push(dropsClone[i]);
}
}
this.drops = newDrops;
window.requestAnimFrame(this.animateDrops.bind(this));
};

/**
 * Polyfill for requestAnimationFrame
 */
RainyDay.prototype.setRequestAnimFrame = function() {
var fps = this.options.fps;
window.requestAnimFrame = (function() {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / fps);
};
})();
};

/**
 * Create the helper canvas for rendering raindrop reflections.
 */
RainyDay.prototype.prepareReflections = function() {
this.reflected = document.createElement('canvas');
this.reflected.width = this.canvas.width / this.options.reflectionScaledownFactor;
this.reflected.height = this.canvas.height / this.options.reflectionScaledownFactor;
var ctx = this.reflected.getContext('2d');
ctx.drawImage(this.img, this.options.crop[0], this.options.crop[1], this.options.crop[2], this.options.crop[3], 0, 0, this.reflected.width, this.reflected.height);
};

/**
 * Create the glass canvas.
 */
RainyDay.prototype.prepareGlass = function() {
this.glass = document.createElement('canvas');
this.glass.width = this.canvas.width;
this.glass.height = this.canvas.height;
this.context = this.glass.getContext('2d');
};

/**
 * Main function for starting rain rendering.
 * @param presets list of presets to be applied
 * @param speed speed of the animation (if not provided or 0 static image will be generated)
 */
RainyDay.prototype.rain = function(presets, speed) {
// prepare canvas for drop reflections
if (this.reflection !== this.REFLECTION_NONE) {
this.prepareReflections();
}

this.animateDrops();

// animation
this.presets = presets;

this.PRIVATE_GRAVITY_FORCE_FACTOR_Y = (this.options.fps * 0.001) / 25;
this.PRIVATE_GRAVITY_FORCE_FACTOR_X = ((Math.PI / 2) - this.options.gravityAngle) * (this.options.fps * 0.001) / 50;

// prepare gravity matrix
if (this.options.enableCollisions) {

// calculate max radius of a drop to establish gravity matrix resolution
var maxDropRadius = 0;
for (var i = 0; i < presets.length; i++) {
if (presets[i][0] + presets[i][1] > maxDropRadius) {
maxDropRadius = Math.floor(presets[i][0] + presets[i][1]);
}
}

if (maxDropRadius > 0) {
// initialize the gravity matrix
var mwi = Math.ceil(this.canvas.width / maxDropRadius);
var mhi = Math.ceil(this.canvas.height / maxDropRadius);
this.matrix = new CollisionMatrix(mwi, mhi, maxDropRadius);
} else {
this.options.enableCollisions = false;
}
}

for (var i = 0; i < presets.length; i++) {
if (!presets[i][3]) {
presets[i][3] = -1;
}
}

var lastExecutionTime = 0;
this.addDropCallback = function() {
var timestamp = new Date().getTime();
if (timestamp - lastExecutionTime < speed) {
return;
}
lastExecutionTime = timestamp;
var context = this.canvas.getContext('2d');
context.clearRect(0, 0, this.canvas.width, this.canvas.height);
context.drawImage(this.background, 0, 0, this.canvas.width, this.canvas.height);
// select matching preset
var preset;
for (var i = 0; i < presets.length; i++) {
if (presets[i][2] > 1 || presets[i][3] === -1) {
if (presets[i][3] !== 0) {
presets[i][3]--;
for (var y = 0; y < presets[i][2]; ++y) {
this.putDrop(new Drop(this, Math.random() * this.canvas.width, Math.random() * this.canvas.height, presets[i][0], presets[i][1]));
}
}
} else if (Math.random() < presets[i][2]) {
preset = presets[i];
break;
}
}
if (preset) {
this.putDrop(new Drop(this, Math.random() * this.canvas.width, Math.random() * this.canvas.height, preset[0], preset[1]));
}
context.save();
context.globalAlpha = this.opacity;
context.drawImage(this.glass, 0, 0, this.canvas.width, this.canvas.height);
context.restore();
}
.bind(this);
};

/**
 * Adds a new raindrop to the animation.
 * @param drop drop object to be added to the animation
 */
RainyDay.prototype.putDrop = function(drop) {
drop.draw();
if (this.gravity && drop.r > this.options.gravityThreshold) {
if (this.options.enableCollisions) {
this.matrix.update(drop);
}
this.drops.push(drop);
}
};

/**
 * Clear the drop and remove from the list if applicable.
 * @drop to be cleared
 * @force force removal from the list
 * result if true animation of this drop should be stopped
 */
RainyDay.prototype.clearDrop = function(drop, force) {
var result = drop.clear(force);
if (result) {
var index = this.drops.indexOf(drop);
if (index >= 0) {
this.drops.splice(index, 1);
}
}
return result;
};

/**
 * Defines a new raindrop object.
 * @param rainyday reference to the parent object
 * @param centerX x position of the center of this drop
 * @param centerY y position of the center of this drop
 * @param min minimum size of a drop
 * @param base base value for randomizing drop size
 */

function Drop(rainyday, centerX, centerY, min, base) {
this.x = Math.floor(centerX);
this.y = Math.floor(centerY);
this.r = (Math.random() * base) + min;
this.rainyday = rainyday;
this.context = rainyday.context;
this.reflection = rainyday.reflected;
}

/**
 * Draws a raindrop on canvas at the current position.
 */
Drop.prototype.draw = function() {
this.context.save();
this.context.beginPath();

var orgR = this.r;
this.r = 0.95 * this.r;
if (this.r < 3) {
this.context.arc(this.x, this.y, this.r, 0, Math.PI * 2, true);
this.context.closePath();
} else if (this.colliding || this.yspeed > 2) {
if (this.colliding) {
var collider = this.colliding;
this.r = 1.001 * (this.r > collider.r ? this.r : collider.r);
this.x += (collider.x - this.x);
this.colliding = null;
}

var yr = 1 + 0.1 * this.yspeed;
this.context.moveTo(this.x - this.r / yr, this.y);
this.context.bezierCurveTo(this.x - this.r, this.y - this.r * 2, this.x + this.r, this.y - this.r * 2, this.x + this.r / yr, this.y);
this.context.bezierCurveTo(this.x + this.r, this.y + yr * this.r, this.x - this.r, this.y + yr * this.r, this.x - this.r / yr, this.y);
} else {
this.context.arc(this.x, this.y, this.r * 0.9, 0, Math.PI * 2, true);
this.context.closePath();
}

this.context.clip();

this.r = orgR;

if (this.rainyday.reflection) {
this.rainyday.reflection(this);
}

this.context.restore();
};

/**
 * Clears the raindrop region.
 * @param force force stop
 * @returns Boolean true if the animation is stopped
 */
Drop.prototype.clear = function(force) {
this.context.clearRect(this.x - this.r - 1, this.y - this.r - 2, 2 * this.r + 2, 2 * this.r + 2);
if (force) {
this.terminate = true;
return true;
}
if ((this.y - this.r > this.rainyday.h) || (this.x - this.r > this.rainyday.w) || (this.x + this.r < 0)) {
// over edge so stop this drop
return true;
}
return false;
};

/**
 * Moves the raindrop to a new position according to the gravity.
 */
Drop.prototype.animate = function() {
if (this.terminate) {
return false;
}
var stopped = this.rainyday.gravity(this);
if (!stopped && this.rainyday.trail) {
this.rainyday.trail(this);
}
if (this.rainyday.options.enableCollisions) {
var collisions = this.rainyday.matrix.update(this, stopped);
if (collisions) {
this.rainyday.collision(this, collisions);
}
}
return !stopped || this.terminate;
};

/**
 * TRAIL function: no trail at all
 */
RainyDay.prototype.TRAIL_NONE = function() {
// nothing going on here
};

/**
 * TRAIL function: trail of small drops (default)
 * @param drop raindrop object
 */
RainyDay.prototype.TRAIL_DROPS = function(drop) {
if (!drop.trailY || drop.y - drop.trailY >= Math.random() * 100 * drop.r) {
drop.trailY = drop.y;
this.putDrop(new Drop(this, drop.x + (Math.random() * 2 - 1) * Math.random(), drop.y - drop.r - 5, Math.ceil(drop.r / 5), 0));
}
};

/**
 * TRAIL function: trail of unblurred image
 * @param drop raindrop object
 */
RainyDay.prototype.TRAIL_SMUDGE = function(drop) {
var y = drop.y - drop.r - 3;
var x = drop.x - drop.r / 2 + (Math.random() * 2);
if (y < 0 || x < 0) {
return;
}
this.context.drawImage(this.clearbackground, x, y, drop.r, 2, x, y, drop.r, 2);
};

/**
 * GRAVITY function: no gravity at all
 * @returns Boolean true if the animation is stopped
 */
RainyDay.prototype.GRAVITY_NONE = function() {
return true;
};

/**
 * GRAVITY function: linear gravity
 * @param drop raindrop object
 * @returns Boolean true if the animation is stopped
 */
RainyDay.prototype.GRAVITY_LINEAR = function(drop) {
if (this.clearDrop(drop)) {
return true;
}

if (drop.yspeed) {
drop.yspeed += this.PRIVATE_GRAVITY_FORCE_FACTOR_Y * Math.floor(drop.r);
drop.xspeed += this.PRIVATE_GRAVITY_FORCE_FACTOR_X * Math.floor(drop.r);
} else {
drop.yspeed = this.PRIVATE_GRAVITY_FORCE_FACTOR_Y;
drop.xspeed = this.PRIVATE_GRAVITY_FORCE_FACTOR_X;
}

drop.y += drop.yspeed;
drop.draw();
return false;
};

/**
 * GRAVITY function: non-linear gravity (default)
 * @param drop raindrop object
 * @returns Boolean true if the animation is stopped
 */
RainyDay.prototype.GRAVITY_NON_LINEAR = function(drop) {
if (this.clearDrop(drop)) {
return true;
}

if (drop.collided) {
drop.collided = false;
drop.seed = Math.floor(drop.r * Math.random() * this.options.fps);
drop.skipping = false;
drop.slowing = false;
} else if (!drop.seed || drop.seed < 0) {
drop.seed = Math.floor(drop.r * Math.random() * this.options.fps);
drop.skipping = drop.skipping === false ? true : false;
drop.slowing = true;
}

drop.seed--;

if (drop.yspeed) {
if (drop.slowing) {
drop.yspeed /= 1.1;
drop.xspeed /= 1.1;
if (drop.yspeed < this.PRIVATE_GRAVITY_FORCE_FACTOR_Y) {
drop.slowing = false;
}

} else if (drop.skipping) {
drop.yspeed = this.PRIVATE_GRAVITY_FORCE_FACTOR_Y;
drop.xspeed = this.PRIVATE_GRAVITY_FORCE_FACTOR_X;
} else {
drop.yspeed += 1 * this.PRIVATE_GRAVITY_FORCE_FACTOR_Y * Math.floor(drop.r);
drop.xspeed += 1 * this.PRIVATE_GRAVITY_FORCE_FACTOR_X * Math.floor(drop.r);
}
} else {
drop.yspeed = this.PRIVATE_GRAVITY_FORCE_FACTOR_Y;
drop.xspeed = this.PRIVATE_GRAVITY_FORCE_FACTOR_X;
}

if (this.options.gravityAngleVariance !== 0) {
drop.xspeed += ((Math.random() * 2 - 1) * drop.yspeed * this.options.gravityAngleVariance);
}

drop.y += drop.yspeed;
drop.x += drop.xspeed;

drop.draw();
return false;
};

/**
 * Utility function to return positive min value
 * @param val1 first number
 * @param val2 second number
 */
RainyDay.prototype.positiveMin = function(val1, val2) {
var result = 0;
if (val1 < val2) {
if (val1 <= 0) {
result = val2;
} else {
result = val1;
}
} else {
if (val2 <= 0) {
result = val1;
} else {
result = val2;
}
}
return result <= 0 ? 1 : result;
};

/**
 * REFLECTION function: no reflection at all
 */
RainyDay.prototype.REFLECTION_NONE = function() {
this.context.fillStyle = this.options.fillStyle;
this.context.fill();
};

/**
 * REFLECTION function: miniature reflection (default)
 * @param drop raindrop object
 */
RainyDay.prototype.REFLECTION_MINIATURE = function(drop) {
var sx = Math.max((drop.x - this.options.reflectionDropMappingWidth) / this.options.reflectionScaledownFactor, 0);
var sy = Math.max((drop.y - this.options.reflectionDropMappingHeight) / this.options.reflectionScaledownFactor, 0);
var sw = this.positiveMin(this.options.reflectionDropMappingWidth * 2 / this.options.reflectionScaledownFactor, this.reflected.width - sx);
var sh = this.positiveMin(this.options.reflectionDropMappingHeight * 2 / this.options.reflectionScaledownFactor, this.reflected.height - sy);
var dx = Math.max(drop.x - 1.1 * drop.r, 0);
var dy = Math.max(drop.y - 1.1 * drop.r, 0);
this.context.drawImage(this.reflected, sx, sy, sw, sh, dx, dy, drop.r * 2, drop.r * 2);
};

/**
 * COLLISION function: default collision implementation
 * @param drop one of the drops colliding
 * @param collisions list of potential collisions
 */
RainyDay.prototype.COLLISION_SIMPLE = function(drop, collisions) {
var item = collisions;
var drop2;
while (item != null) {
var p = item.drop;
if (Math.sqrt(Math.pow(drop.x - p.x, 2) + Math.pow(drop.y - p.y, 2)) < (drop.r + p.r)) {
drop2 = p;
break;
}
item = item.next;
}

if (!drop2) {
return;
}

// rename so that we're dealing with low/high drops
var higher,
lower;
if (drop.y > drop2.y) {
higher = drop;
lower = drop2;
} else {
higher = drop2;
lower = drop;
}

this.clearDrop(lower);
// force stopping the second drop
this.clearDrop(higher, true);
this.matrix.remove(higher);
lower.draw();

lower.colliding = higher;
lower.collided = true;
};

/**
 * Resizes canvas, draws original image and applies blurring algorithm.
 */
RainyDay.prototype.prepareBackground = function() {
this.background = document.createElement('canvas');
this.background.width = this.canvas.width;
this.background.height = this.canvas.height;

this.clearbackground = document.createElement('canvas');
this.clearbackground.width = this.canvas.width;
this.clearbackground.height = this.canvas.height;

var context = this.background.getContext('2d');
context.clearRect(0, 0, this.canvas.width, this.canvas.height);

context.drawImage(this.img, this.options.crop[0], this.options.crop[1], this.options.crop[2], this.options.crop[3], 0, 0, this.canvas.width, this.canvas.height);

context = this.clearbackground.getContext('2d');
context.clearRect(0, 0, this.canvas.width, this.canvas.height);
context.drawImage(this.img, this.options.crop[0], this.options.crop[1], this.options.crop[2], this.options.crop[3], 0, 0, this.canvas.width, this.canvas.height);

if (!isNaN(this.options.blur) && this.options.blur >= 1) {
this.stackBlurCanvasRGB(this.canvas.width, this.canvas.height, this.options.blur);
}
};

/**
 * Implements the Stack Blur Algorithm (@see http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html).
 * @param width width of the canvas
 * @param height height of the canvas
 * @param radius blur radius
 */
RainyDay.prototype.stackBlurCanvasRGB = function(width, height, radius) {

var shgTable = [
[0, 9],
[1, 11],
[2, 12],
[3, 13],
[5, 14],
[7, 15],
[11, 16],
[15, 17],
[22, 18],
[31, 19],
[45, 20],
[63, 21],
[90, 22],
[127, 23],
[181, 24]
];

var mulTable = [
512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292, 512,
454, 405, 364, 328, 298, 271, 496, 456, 420, 388, 360, 335, 312, 292, 273, 512,
482, 454, 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259, 496, 475, 456,
437, 420, 404, 388, 374, 360, 347, 335, 323, 312, 302, 292, 282, 273, 265, 512,
497, 482, 468, 454, 441, 428, 417, 405, 394, 383, 373, 364, 354, 345, 337, 328,
320, 312, 305, 298, 291, 284, 278, 271, 265, 259, 507, 496, 485, 475, 465, 456,
446, 437, 428, 420, 412, 404, 396, 388, 381, 374, 367, 360, 354, 347, 341, 335,
329, 323, 318, 312, 307, 302, 297, 292, 287, 282, 278, 273, 269, 265, 261, 512,
505, 497, 489, 482, 475, 468, 461, 454, 447, 441, 435, 428, 422, 417, 411, 405,
399, 394, 389, 383, 378, 373, 368, 364, 359, 354, 350, 345, 341, 337, 332, 328,
324, 320, 316, 312, 309, 305, 301, 298, 294, 291, 287, 284, 281, 278, 274, 271,
268, 265, 262, 259, 257, 507, 501, 496, 491, 485, 480, 475, 470, 465, 460, 456,
451, 446, 442, 437, 433, 428, 424, 420, 416, 412, 408, 404, 400, 396, 392, 388,
385, 381, 377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344, 341, 338, 335,
332, 329, 326, 323, 320, 318, 315, 312, 310, 307, 304, 302, 299, 297, 294, 292,
289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259
];

radius |= 0;

var context = this.background.getContext('2d');
var imageData = context.getImageData(0, 0, width, height);
var pixels = imageData.data;
var x,
y,
i,
p,
yp,
yi,
yw,
rSum,
gSum,
bSum,
rOutSum,
gOutSum,
bOutSum,
rInSum,
gInSum,
bInSum,
pr,
pg,
pb,
rbs;
var radiusPlus1 = radius + 1;
var sumFactor = radiusPlus1 * (radiusPlus1 + 1) / 2;

var stackStart = new BlurStack();
var stackEnd = new BlurStack();
var stack = stackStart;
for (i = 1; i < 2 * radius + 1; i++) {
stack = stack.next = new BlurStack();
if (i === radiusPlus1) {
stackEnd = stack;
}
}
stack.next = stackStart;
var stackIn = null;
var stackOut = null;

yw = yi = 0;

var mulSum = mulTable[radius];
var shgSum;
for (var ssi = 0; ssi < shgTable.length; ++ssi) {
if (radius <= shgTable[ssi][0]) {
shgSum = shgTable[ssi - 1][1];
break;
}
}

for (y = 0; y < height; y++) {
rInSum = gInSum = bInSum = rSum = gSum = bSum = 0;

rOutSum = radiusPlus1 * (pr = pixels[yi]);
gOutSum = radiusPlus1 * (pg = pixels[yi + 1]);
bOutSum = radiusPlus1 * (pb = pixels[yi + 2]);

rSum += sumFactor * pr;
gSum += sumFactor * pg;
bSum += sumFactor * pb;

stack = stackStart;

for (i = 0; i < radiusPlus1; i++) {
stack.r = pr;
stack.g = pg;
stack.b = pb;
stack = stack.next;
}

for (i = 1; i < radiusPlus1; i++) {
p = yi + ((width - 1 < i ? width - 1 : i) << 2);
rSum += (stack.r = (pr = pixels[p])) * (rbs = radiusPlus1 - i);
gSum += (stack.g = (pg = pixels[p + 1])) * rbs;
bSum += (stack.b = (pb = pixels[p + 2])) * rbs;

rInSum += pr;
gInSum += pg;
bInSum += pb;

stack = stack.next;
}

stackIn = stackStart;
stackOut = stackEnd;
for (x = 0; x < width; x++) {
pixels[yi] = (rSum * mulSum) >> shgSum;
pixels[yi + 1] = (gSum * mulSum) >> shgSum;
pixels[yi + 2] = (bSum * mulSum) >> shgSum;

rSum -= rOutSum;
gSum -= gOutSum;
bSum -= bOutSum;

rOutSum -= stackIn.r;
gOutSum -= stackIn.g;
bOutSum -= stackIn.b;

p = (yw + ((p = x + radius + 1) < (width - 1) ? p : (width - 1))) << 2;

rInSum += (stackIn.r = pixels[p]);
gInSum += (stackIn.g = pixels[p + 1]);
bInSum += (stackIn.b = pixels[p + 2]);

rSum += rInSum;
gSum += gInSum;
bSum += bInSum;

stackIn = stackIn.next;

rOutSum += (pr = stackOut.r);
gOutSum += (pg = stackOut.g);
bOutSum += (pb = stackOut.b);

rInSum -= pr;
gInSum -= pg;
bInSum -= pb;

stackOut = stackOut.next;

yi += 4;
}
yw += width;
}

for (x = 0; x < width; x++) {
gInSum = bInSum = rInSum = gSum = bSum = rSum = 0;

yi = x << 2;
rOutSum = radiusPlus1 * (pr = pixels[yi]);
gOutSum = radiusPlus1 * (pg = pixels[yi + 1]);
bOutSum = radiusPlus1 * (pb = pixels[yi + 2]);

rSum += sumFactor * pr;
gSum += sumFactor * pg;
bSum += sumFactor * pb;

stack = stackStart;

for (i = 0; i < radiusPlus1; i++) {
stack.r = pr;
stack.g = pg;
stack.b = pb;
stack = stack.next;
}

yp = width;

for (i = 1; i < radiusPlus1; i++) {
yi = (yp + x) << 2;

rSum += (stack.r = (pr = pixels[yi])) * (rbs = radiusPlus1 - i);
gSum += (stack.g = (pg = pixels[yi + 1])) * rbs;
bSum += (stack.b = (pb = pixels[yi + 2])) * rbs;

rInSum += pr;
gInSum += pg;
bInSum += pb;

stack = stack.next;

if (i < (height - 1)) {
yp += width;
}
}

yi = x;
stackIn = stackStart;
stackOut = stackEnd;
for (y = 0; y < height; y++) {
p = yi << 2;
pixels[p] = (rSum * mulSum) >> shgSum;
pixels[p + 1] = (gSum * mulSum) >> shgSum;
pixels[p + 2] = (bSum * mulSum) >> shgSum;

rSum -= rOutSum;
gSum -= gOutSum;
bSum -= bOutSum;

rOutSum -= stackIn.r;
gOutSum -= stackIn.g;
bOutSum -= stackIn.b;

p = (x + (((p = y + radiusPlus1) < (height - 1) ? p : (height - 1)) * width)) << 2;

rSum += (rInSum += (stackIn.r = pixels[p]));
gSum += (gInSum += (stackIn.g = pixels[p + 1]));
bSum += (bInSum += (stackIn.b = pixels[p + 2]));

stackIn = stackIn.next;

rOutSum += (pr = stackOut.r);
gOutSum += (pg = stackOut.g);
bOutSum += (pb = stackOut.b);

rInSum -= pr;
gInSum -= pg;
bInSum -= pb;

stackOut = stackOut.next;

yi += width;
}
}

context.putImageData(imageData, 0, 0);

};

Hi I am not sure what is wrong with my rainyday.min.js code but it doesnt seem to work in firefox. In fact, it redirected me to a 404 page in my site. The code is tested and am working well with Safari and Chrome. Please assist. Thank you.

/**
 * Defines a new helper object for Stack Blur Algorithm.
 */
function BlurStack() {
this.r = 0;
this.g = 0;
this.b = 0;
this.next = null;
}

/**
 * Defines a gravity matrix object which handles collision detection.
 * @param x number of columns in the matrix
 * @param y number of rows in the matrix
 * @param r grid size
 */
function CollisionMatrix(x, y, r) {
this.resolution = r;
this.xc = x;
this.yc = y;
this.matrix = new Array(x);
for (var i = 0; i <= (x + 5); i++) {
this.matrix[i] = new Array(y);
for (var j = 0; j <= (y + 5); ++j) {
this.matrix[i][j] = new DropItem(null);
}
}
}

/**
 * Updates position of the given drop on the collision matrix.
 * @param drop raindrop to be positioned/repositioned
 * @param forceDelete if true the raindrop will be removed from the matrix
 * @returns collisions if any
 */
CollisionMatrix.prototype.update = function(drop, forceDelete) {
if (drop.gid) {
if (!this.matrix[drop.gmx] || !this.matrix[drop.gmx][drop.gmy]) {
return null;
}
this.matrix[drop.gmx][drop.gmy].remove(drop);
if (forceDelete) {
return null;
}

drop.gmx = Math.floor(drop.x / this.resolution);
drop.gmy = Math.floor(drop.y / this.resolution);
if (!this.matrix[drop.gmx] || !this.matrix[drop.gmx][drop.gmy]) {
return null;
}
this.matrix[drop.gmx][drop.gmy].add(drop);

var collisions = this.collisions(drop);
if (collisions && collisions.next != null) {
return collisions.next;
}
} else {
drop.gid = Math.random().toString(36).substr(2, 9);
drop.gmx = Math.floor(drop.x / this.resolution);
drop.gmy = Math.floor(drop.y / this.resolution);
if (!this.matrix[drop.gmx] || !this.matrix[drop.gmx][drop.gmy]) {
return null;
}

this.matrix[drop.gmx][drop.gmy].add(drop);
}
return null;
};

/**
 * Looks for collisions with the given raindrop.
 * @param drop raindrop to be checked
 * @returns DropItem list of drops that collide with it
 */
CollisionMatrix.prototype.collisions = function(drop) {
var item = new DropItem(null);
var first = item;

item = this.addAll(item, drop.gmx - 1, drop.gmy + 1);
item = this.addAll(item, drop.gmx, drop.gmy + 1);
item = this.addAll(item, drop.gmx + 1, drop.gmy + 1);

return first;
};

/**
 * Appends all found drop at a given location to the given item.
 * @param to item to which the results will be appended to
 * @param x x position in the matrix
 * @param y y position in the matrix
 * @returns last discovered item on the list
 */
CollisionMatrix.prototype.addAll = function(to, x, y) {
if (x > 0 && y > 0 && x < this.xc && y < this.yc) {
var items = this.matrix[x][y];
while (items.next != null) {
items = items.next;
to.next = new DropItem(items.drop);
to = to.next;
}
}
return to;
};

/**
 * Removed the drop from its current position
 * @param drop to be removed
 */
CollisionMatrix.prototype.remove = function(drop) {
this.matrix[drop.gmx][drop.gmy].remove(drop);
};

/**
 * Defines a linked list item.
 */
function DropItem(drop) {
this.drop = drop;
this.next = null;
}

/**
 * Adds the raindrop to the end of the list.
 * @param drop raindrop to be added
 */
DropItem.prototype.add = function(drop) {
var item = this;
while (item.next != null) {
item = item.next;
}
item.next = new DropItem(drop);
};

/**
 * Removes the raindrop from the list.
 * @param drop raindrop to be removed
 */
DropItem.prototype.remove = function(drop) {
var item = this;
var prevItem = null;
while (item.next != null) {
prevItem = item;
item = item.next;
if (item.drop.gid === drop.gid) {
prevItem.next = item.next;
}
}
};

// Veented part below:

function vntd_rain(rainy_el) {

    var image = rainy_el;
    
    var parentID = image.closest('section').attr('id');
    var parent = document.getElementById(parentID);
    
    image.bind('load',function() {
    var engine = new RainyDay({
       image: this,
       enableSizeChange: true,
       parentElement: parent,
       fps: 24,
       blur: 9
    });
    engine.trail = engine.TRAIL_SMUDGE;
    engine.rain([ [5, 4, 0.1], [2, 1.8, 0.1] ], 60 );
    });
    
    image.crossOrigin = 'anonymous';
    
    var img_url = jQuery('#'+parentID+' .vc_row').css('background-image');
    img_url = img_url.replace('url(','').replace(')','');
    
    image.attr('src', img_url);

var fullsize = image;

function vntdResize(el) {
 
 	var imgwidth = el.width();
var imgheight = el.height();
var winwidth = jQuery(window).width();
var winheight = jQuery(window).height();
var widthratio = winwidth / imgwidth;
var heightratio = winheight / imgheight;
var widthdiff = heightratio * imgwidth;
var heightdiff = widthratio * imgheight;
if(heightdiff>winheight) {
el.css({
width: winwidth+'px',
height: heightdiff+'px'
});
} else {
el.css({
width: widthdiff+'px',
height: winheight+'px'
});
}
} 
vntdResize(image);
vntdResize(jQuery('canvas'));
jQuery(window).resize(function(){
vntdResize(image);
vntdResize(jQuery('canvas'));
});
    
}

jQuery(window).bind('load',function(){
jQuery('.rainy-background').each(function() {
vntd_rain(jQuery(this));
});
});

Do you have a demo page to help us investigate the issue?

Hi

Thank you for replying. Here is a sample: http://veented.com/themes/north/home13/

In chrome, the rain works well, but in firefox, there is a major bug.

Thanks in advance.

The good news is that rainyday.js works in Firefox, as can be seen in this demo

The rainyday.js file that your site is using looks different from the official one found at https://github.com/maroslaw/rainyday.js

From the custom changes that you have made to rainyday.js (never a good idea as it leads to confusion with the original, always use a separate script file instead), the value in img_url is where you’re going wrong.

unescaping the value there should be your path to salvation.

Hi

I got no knowledge on javascript, could you please assist me further? From your response, i tried to comment out the img_url section but no changes, in fact, firefox still redirect me to my 404 page. Appreciate if I could use the same code as i prefer the effect than the demo provided. Thanks in advance!

The issue is that Firefox behaves differently when it comes to retrieving the CSS values.
With Firefox, the double quotes are escaped and look like /"

You should be able to solve this particular problem by escaping the img_url value.
For example:

img_url = ... // initial line of code here
img_url = escape(img_url);

Hi

I’m not sure if im doing it right but i supposed im have to do this:

var img_url = jQuery('#'+parentID+' .vc_row').css('background-image');
    img_url = img_url.replace('url(','').replace(')','');
	img_url = escape(img_url);

That doesnt work.

I see that your page hasn’t been updated with the modification, so nothing further can be learned from that yet.

Instead, here’s another approach.

Firefox quotes the url content in double quotes, whereas chrome and other browsers leave it alone.

Firefox: url(“http://veented.com/themes/north/wp-content/uploads/2014/09/rain-bg.jpg?id=1039”)
Chrome: url(http://veented.com/themes/north/wp-content/uploads/2014/09/rain-bg.jpg?id=1039)

There are a couple of different approaches to this. One approach is to strip off both ends leaving the content within. Another approach is to extract the goodness from within.

With the first approach where you remove the outer ends, you need to make it flexible enough to handle the quotes that are there in Firefox but not elsewhere.

Starting from:

img_url = img_url.replace('url(','').replace(')','');

we can replace those strings with a regular expression, so that we can then improve on them.

img_url = img_url.replace(/url\(/,'').replace(/\)/,'');

Now we can use "? to also match the optional double quote

img_url = img_url.replace(/url\(\"?/,'').replace(/\"?\)/,'');

The other approach which is my preferred one, is to match the http part inside, which ends with the id and multiple numbers

img_url = img_url.match(/(http.*id=\d*)/)[1];

That’s the approach that I’d take to resolve the problem. Both solutions work, but the last one tends to be more stable and focuses on what you are wanting to retrieve.

Hi

I am actually testing my side. And it is currently disabled in development mode. I tried both code and it doesnt work as well.

When i do this:

var img_url = jQuery(‘#’+parentID+’ .vc_row’).css(‘background-image’);
img_url = img_url.replace(‘url(’,‘’).replace(‘)’,‘’);
img_url = img_url.replace(‘url(’,‘’).replace(‘)’,‘’);
img_url = img_url.replace(/url(/,‘’).replace(/)/,‘’);
img_url = img_url.replace(/url("?/,‘’).replace(/"?)/,‘’);

The URL in firefox shows:

mywebsitelink.com/wp-content/uploads/2014/07/i41.jpg?id=539

then after a few second it refreshes and show this:

mywebsitelink.com/wp-content/uploads/2014/07/none

When i tried the preferred code, this is what i get:

mywebsitelink.com/wp-content/uploads/2014/07/i41.jpg?id=539

Both code resulted in Page Not Found

My website is inPixelHaus btw.

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.