File: //var/www/shoetique/wp-content/themes/north-wp/assets/js/vendor/background-check.js
/*
* BackgroundCheck
* http://kennethcachia.com/background-check
*
* v1.2.2
*/
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(factory);
} else {
root.BackgroundCheck = factory(root);
}
}(this, function () {
'use strict';
var resizeEvent = window.orientation !== undefined ? 'orientationchange' : 'resize';
var supported;
var canvas;
var context;
var throttleDelay;
var viewport;
var attrs = {};
/*
* Initializer
*/
function init(a) {
if (a === undefined || a.targets === undefined) {
throw 'Missing attributes';
}
// Default values
attrs.debug = checkAttr(a.debug, false);
attrs.debugOverlay = checkAttr(a.debugOverlay, false);
attrs.targets = getElements(a.targets);
attrs.images = getElements(a.images || 'img', true);
attrs.changeParent = checkAttr(a.changeParent, false);
attrs.threshold = checkAttr(a.threshold, 50);
attrs.minComplexity = checkAttr(a.minComplexity, 30);
attrs.minOverlap = checkAttr(a.minOverlap, 50);
attrs.windowEvents = checkAttr(a.windowEvents, true);
attrs.maxDuration = checkAttr(a.maxDuration, 500);
attrs.mask = checkAttr(a.mask, {
r: 0,
g: 255,
b: 0
});
attrs.classes = checkAttr(a.classes, {
dark: 'background--dark',
light: 'background--light',
complex: 'background--complex'
});
if (supported === undefined) {
checkSupport();
if (supported) {
canvas.style.position = 'fixed';
canvas.style.top = '0px';
canvas.style.left = '0px';
canvas.style.width = '100%';
canvas.style.height = '100%';
window.addEventListener(resizeEvent, throttle.bind(null, function () {
resizeCanvas();
check();
}));
window.addEventListener('scroll', throttle.bind(null, check));
resizeCanvas();
check();
}
}
}
/*
* Destructor
*/
function destroy() {
supported = null;
canvas = null;
context = null;
attrs = {};
if (throttleDelay) {
clearTimeout(throttleDelay);
}
}
/*
* Output debug logs
*/
function log(msg) {
if (get('debug')) {
console.log(msg);
}
}
/*
* Get attribute value, use a default
* when undefined
*/
function checkAttr(value, def) {
checkType(value, typeof def);
return (value === undefined) ? def : value;
}
/*
* Reject unwanted types
*/
function checkType(value, type) {
if (value !== undefined && typeof value !== type) {
throw 'Incorrect attribute type';
}
}
/*
* Convert elements with background-image
* to Images
*/
function checkForCSSImages(els) {
var el;
var url;
var list = [];
for (var e = 0; e < els.length; e++) {
el = els[e];
list.push(el);
if (el.tagName !== 'IMG') {
url = window.getComputedStyle(el).backgroundImage;
// Ignore multiple backgrounds
if (url.split(/,url|, url/).length > 1) {
throw 'Multiple backgrounds are not supported';
}
if (url && url !== 'none') {
list[e] = {
img: new Image(),
el: list[e]
};
url = url.slice(4, -1);
url = url.replace(/"/g, '');
list[e].img.src = url;
list[e].img.setAttribute('crossorigin', '');
log('CSS Image - ' + url);
} else {
throw 'Element is not an <img> but does not have a background-image';
}
}
}
return list;
}
/*
* Check for String, Element or NodeList
*/
function getElements(selector, convertToImages) {
var els = selector;
if (typeof selector === 'string') {
els = document.querySelectorAll(selector);
} else if (selector && selector.nodeType === 1) {
els = [selector];
}
if (!els || els.length === 0 || els.length === undefined) {
throw 'Elements not found';
} else {
if (convertToImages) {
els = checkForCSSImages(els);
}
els = Array.prototype.slice.call(els);
}
return els;
}
/*
* Check if browser supports <canvas>
*/
function checkSupport() {
canvas = document.createElement('canvas');
if (canvas && canvas.getContext) {
context = canvas.getContext('2d');
supported = true;
} else {
supported = false;
}
showDebugOverlay();
}
/*
* Show <canvas> on top of page
*/
function showDebugOverlay() {
if (get('debugOverlay')) {
canvas.style.opacity = 0.5;
canvas.style.pointerEvents = 'none';
document.body.appendChild(canvas);
} else {
// Check if it was previously added
if (canvas.parentNode) {
canvas.parentNode.removeChild(canvas);
}
}
}
/*
* Stop if it's slow
*/
function kill(start) {
var duration = new Date().getTime() - start;
log('Duration: ' + duration + 'ms');
if (duration > get('maxDuration')) {
// Log a message even when debug is false
console.log('BackgroundCheck - Killed');
removeClasses();
destroy();
}
}
/*
* Set width and height of <canvas>
*/
function resizeCanvas() {
viewport = {
left: 0,
top: 0,
right: document.body.clientWidth,
bottom: window.innerHeight
};
canvas.width = document.body.clientWidth;
canvas.height = window.innerHeight;
}
/*
* Process px and %, discard anything else
*/
function getValue(css, parent, delta) {
var value;
var percentage;
if (css.indexOf('px') !== -1) {
value = parseFloat(css);
} else if (css.indexOf('%') !== -1) {
value = parseFloat(css);
percentage = value / 100;
value = percentage * parent;
if (delta) {
value -= delta * percentage;
}
} else {
value = parent;
}
return value;
}
/*
* Calculate top, left, width and height
* using the object's CSS
*/
function calculateAreaFromCSS(obj) {
var css = window.getComputedStyle(obj.el);
// Force no-repeat and padding-box
obj.el.style.backgroundRepeat = 'no-repeat';
obj.el.style.backgroundOrigin = 'padding-box';
// Background Size
var size = css.backgroundSize.split(' ');
var width = size[0];
var height = size[1] === undefined ? 'auto' : size[1];
var parentRatio = obj.el.clientWidth / obj.el.clientHeight;
var imgRatio = obj.img.naturalWidth / obj.img.naturalHeight;
if (width === 'cover') {
if (parentRatio >= imgRatio) {
width = '100%';
height = 'auto';
} else {
width = 'auto';
size[0] = 'auto';
height = '100%';
}
} else if (width === 'contain') {
if (1 / parentRatio < 1 / imgRatio) {
width = 'auto';
size[0] = 'auto';
height = '100%';
} else {
width = '100%';
height = 'auto';
}
}
if (width === 'auto') {
width = obj.img.naturalWidth;
} else {
width = getValue(width, obj.el.clientWidth);
}
if (height === 'auto') {
height = (width / obj.img.naturalWidth) * obj.img.naturalHeight;
} else {
height = getValue(height, obj.el.clientHeight);
}
if (size[0] === 'auto' && size[1] !== 'auto') {
width = (height / obj.img.naturalHeight) * obj.img.naturalWidth;
}
var position = css.backgroundPosition;
// Fix inconsistencies between browsers
if (position === 'top') {
position = '50% 0%';
} else if (position === 'left') {
position = '0% 50%';
} else if (position === 'right') {
position = '100% 50%';
} else if (position === 'bottom') {
position = '50% 100%';
} else if (position === 'center') {
position = '50% 50%';
}
position = position.split(' ');
var x;
var y;
// Two-value syntax vs Four-value syntax
if (position.length === 4) {
x = position[1];
y = position[3];
} else {
x = position[0];
y = position[1];
}
// Use a default value
y = y || '50%';
// Background Position
x = getValue(x, obj.el.clientWidth, width);
y = getValue(y, obj.el.clientHeight, height);
// Take care of ex: background-position: right 20px bottom 20px;
if (position.length === 4) {
if (position[0] === 'right') {
x = obj.el.clientWidth - obj.img.naturalWidth - x;
}
if (position[2] === 'bottom') {
y = obj.el.clientHeight - obj.img.naturalHeight - y;
}
}
x += obj.el.getBoundingClientRect().left;
y += obj.el.getBoundingClientRect().top;
return {
left: Math.floor(x),
right: Math.floor(x + width),
top: Math.floor(y),
bottom: Math.floor(y + height),
width: Math.floor(width),
height: Math.floor(height)
};
}
/*
* Get Bounding Client Rect
*/
function getArea(obj) {
var area;
var image;
var parent;
if (obj.nodeType) {
var rect = obj.getBoundingClientRect();
// Clone ClientRect for modification purposes
area = {
left: rect.left,
right: rect.right,
top: rect.top,
bottom: rect.bottom,
width: rect.width,
height: rect.height
};
parent = obj.parentNode;
image = obj;
} else {
area = calculateAreaFromCSS(obj);
parent = obj.el;
image = obj.img;
}
parent = parent.getBoundingClientRect();
area.imageTop = 0;
area.imageLeft = 0;
area.imageWidth = image.naturalWidth;
area.imageHeight = image.naturalHeight;
var ratio = area.imageHeight / area.height;
var delta;
// Stay within the parent's boundary
if (area.top < parent.top) {
delta = parent.top - area.top;
area.imageTop = ratio * delta;
area.imageHeight -= ratio * delta;
area.top += delta;
area.height -= delta;
}
if (area.left < parent.left) {
delta = parent.left - area.left;
area.imageLeft += ratio * delta;
area.imageWidth -= ratio * delta;
area.width -= delta;
area.left += delta;
}
if (area.bottom > parent.bottom) {
delta = area.bottom - parent.bottom;
area.imageHeight -= ratio * delta;
area.height -= delta;
}
if (area.right > parent.right) {
delta = area.right - parent.right;
area.imageWidth -= ratio * delta;
area.width -= delta;
}
area.imageTop = Math.floor(area.imageTop);
area.imageLeft = Math.floor(area.imageLeft);
area.imageHeight = Math.floor(area.imageHeight);
area.imageWidth = Math.floor(area.imageWidth);
return area;
}
/*
* Render image on canvas
*/
function drawImage(image) {
var area = getArea(image);
image = image.nodeType ? image : image.img;
if (area.imageWidth > 0 && area.imageHeight > 0 && area.width > 0 && area.height > 0) {
context.drawImage(image,
area.imageLeft, area.imageTop, area.imageWidth, area.imageHeight,
area.left, area.top, area.width, area.height);
} else {
log('Skipping image - ' + image.src + ' - area too small');
}
}
/*
* Add/remove classes
*/
function classList(node, name, mode) {
var className = node.className;
switch (mode) {
case 'add':
className += ' ' + name;
break;
case 'remove':
var pattern = new RegExp('(?:^|\\s)' + name + '(?!\\S)', 'g');
className = className.replace(pattern, '');
break;
}
node.className = className.trim();
}
/*
* Remove classes from element or
* their parents, depending on checkParent
*/
function removeClasses(el) {
var targets = el ? [el] : get('targets');
var target;
for (var t = 0; t < targets.length; t++) {
target = targets[t];
target = get('changeParent') ? target.parentNode : target;
classList(target, get('classes').light, 'remove');
classList(target, get('classes').dark, 'remove');
classList(target, get('classes').complex, 'remove');
}
}
/*
* Calculate average pixel brightness of a region
* and add 'light' or 'dark' accordingly
*/
function calculatePixelBrightness(target) {
var dims = target.getBoundingClientRect();
var brightness;
var data;
var pixels = 0;
var delta;
var deltaSqr = 0;
var mean = 0;
var variance;
var minOverlap = 0;
var mask = get('mask');
if (dims.width > 0 && dims.height > 0) {
removeClasses(target);
target = get('changeParent') ? target.parentNode : target;
data = context.getImageData(dims.left, dims.top, dims.width, dims.height).data;
for (var p = 0; p < data.length; p += 4) {
if (data[p] === mask.r && data[p + 1] === mask.g && data[p + 2] === mask.b) {
minOverlap++;
} else {
pixels++;
brightness = (0.2126 * data[p]) + (0.7152 * data[p + 1]) + (0.0722 * data[p + 2]);
delta = brightness - mean;
deltaSqr += delta * delta;
mean = mean + delta / pixels;
}
}
if (minOverlap <= (data.length / 4) * (1 - (get('minOverlap') / 100))) {
variance = Math.sqrt(deltaSqr / pixels) / 255;
mean = mean / 255;
log('Target: ' + target.className + ' lum: ' + mean + ' var: ' + variance);
classList(target, mean <= (get('threshold') / 100) ? get('classes').dark : get('classes').light, 'add');
if (variance > get('minComplexity') / 100) {
classList(target, get('classes').complex, 'add');
}
}
}
}
/*
* Test if a is within b's boundary
*/
function isInside(a, b) {
a = (a.nodeType ? a : a.el).getBoundingClientRect();
b = b === viewport ? b : (b.nodeType ? b : b.el).getBoundingClientRect();
return !(a.right < b.left || a.left > b.right || a.top > b.bottom || a.bottom < b.top);
}
/*
* Process all targets (checkTarget is undefined)
* or a single target (checkTarget is a previously set target)
*
* When not all images are loaded, checkTarget is an image
* to avoid processing all targets multiple times
*/
function processTargets(checkTarget) {
var start = new Date().getTime();
var mode = (checkTarget && (checkTarget.tagName === 'IMG' || checkTarget.img)) ? 'image' : 'targets';
var found = checkTarget ? false : true;
var total = get('targets').length;
var target;
for (var t = 0; t < total; t++) {
target = get('targets')[t];
if (isInside(target, viewport)) {
if (mode === 'targets' && (!checkTarget || checkTarget === target)) {
found = true;
calculatePixelBrightness(target);
} else if (mode === 'image' && isInside(target, checkTarget)) {
calculatePixelBrightness(target);
}
}
}
if (mode === 'targets' && !found) {
throw checkTarget + ' is not a target';
}
kill(start);
}
/*
* Find the element's zIndex. Also checks
* the zIndex of its parent
*/
function getZIndex(el) {
var calculate = function (el) {
var zindex = 0;
if (window.getComputedStyle(el).position !== 'static') {
zindex = parseInt(window.getComputedStyle(el).zIndex, 10) || 0;
// Reserve zindex = 0 for elements with position: static;
if (zindex >= 0) {
zindex++;
}
}
return zindex;
};
var parent = el.parentNode;
var zIndexParent = parent ? calculate(parent) : 0;
var zIndexEl = calculate(el);
return (zIndexParent * 100000) + zIndexEl;
}
/*
* Check zIndexes
*/
function sortImagesByZIndex(images) {
var sorted = false;
images.sort(function (a, b) {
a = a.nodeType ? a : a.el;
b = b.nodeType ? b : b.el;
var pos = a.compareDocumentPosition(b);
var reverse = 0;
a = getZIndex(a);
b = getZIndex(b);
if (a > b) {
sorted = true;
}
// Reposition if zIndex is the same but the elements are not
// sorted according to their document position
if (a === b && pos === 2) {
reverse = 1;
} else if (a === b && pos === 4) {
reverse = -1;
}
return reverse || a - b;
});
log('Sorted: ' + sorted);
if (sorted) {
log(images);
}
return sorted;
}
/*
* Main function
*/
function check(target, avoidClear, imageLoaded) {
if (supported) {
var mask = get('mask');
log('--- BackgroundCheck ---');
log('onLoad event: ' + (imageLoaded && imageLoaded.src));
if (avoidClear !== true) {
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = 'rgb(' + mask.r + ', ' + mask.g + ', ' + mask.b + ')';
context.fillRect(0, 0, canvas.width, canvas.height);
}
var processImages = imageLoaded ? [imageLoaded] : get('images');
var sorted = sortImagesByZIndex(processImages);
var image;
var imageNode;
var loading = false;
for (var i = 0; i < processImages.length; i++) {
image = processImages[i];
if (isInside(image, viewport)) {
imageNode = image.nodeType ? image : image.img;
if (imageNode.naturalWidth === 0) {
loading = true;
log('Loading... ' + image.src);
imageNode.removeEventListener('load', check);
if (sorted) {
// Sorted -- redraw all images
imageNode.addEventListener('load', check.bind(null, null, false, null));
} else {
// Not sorted -- just draw one image
imageNode.addEventListener('load', check.bind(null, target, true, image));
}
} else {
log('Drawing: ' + image.src);
drawImage(image);
}
}
}
if (!imageLoaded && !loading) {
processTargets(target);
} else if (imageLoaded) {
processTargets(imageLoaded);
}
}
}
/*
* Throttle events
*/
function throttle(callback) {
if (get('windowEvents') === true) {
if (throttleDelay) {
clearTimeout(throttleDelay);
}
throttleDelay = setTimeout(callback, 200);
}
}
/*
* Setter
*/
function set(property, newValue) {
if (attrs[property] === undefined) {
throw 'Unknown property - ' + property;
} else if (newValue === undefined) {
throw 'Missing value for ' + property;
}
if (property === 'targets' || property === 'images') {
try {
newValue = getElements(property === 'images' && !newValue ? 'img' : newValue, property === 'images' ? true : false);
} catch (err) {
newValue = [];
throw err;
}
} else {
checkType(newValue, typeof attrs[property]);
}
removeClasses();
attrs[property] = newValue;
check();
if (property === 'debugOverlay') {
showDebugOverlay();
}
}
/*
* Getter
*/
function get(property) {
if (attrs[property] === undefined) {
throw 'Unknown property - ' + property;
}
return attrs[property];
}
/*
* Get position and size of all images.
* Used for testing purposes
*/
function getImageData() {
var images = get('images');
var area;
var data = [];
for (var i = 0; i < images.length; i++) {
area = getArea(images[i]);
data.push(area);
}
return data;
}
return {
/*
* Init and destroy
*/
init: init,
destroy: destroy,
/*
* Expose main function
*/
refresh: check,
/*
* Setters and getters
*/
set: set,
get: get,
/*
* Return image data
*/
getImageData: getImageData
};
}));