From 06dbd7d41caa01b365b27f81b201184d6f54e380 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <jerome@nexedi.com>
Date: Thu, 4 Oct 2018 09:25:04 +0200
Subject: [PATCH] Zelenium: add resemble.js 2.10.2 and make it available in
 test namespace
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

from https://github.com/HuddleEng/Resemble.js/blob/v2.10.2/resemble.js © 2013 Huddle
---
 product/Zelenium/selenium/core/TestRunner.hta |    1 +
 .../Zelenium/selenium/core/TestRunner.html    |    1 +
 .../Zelenium/selenium/core/lib/resemble.js    | 1032 +++++++++++++++++
 3 files changed, 1034 insertions(+)
 create mode 100644 product/Zelenium/selenium/core/lib/resemble.js

diff --git a/product/Zelenium/selenium/core/TestRunner.hta b/product/Zelenium/selenium/core/TestRunner.hta
index f11cdf4ce1..78d5ed2c5f 100644
--- a/product/Zelenium/selenium/core/TestRunner.hta
+++ b/product/Zelenium/selenium/core/TestRunner.hta
@@ -48,6 +48,7 @@ to work-around a bug in IE on Win2K whereby the HTA application doesn't function
         <script language="JavaScript" type="text/javascript" src="xpath/dom.js"></script>
         <script language="JavaScript" type="text/javascript" src="xpath/xpath.js"></script>
         <script language="JavaScript" type="text/javascript" src="xpath/javascript-xpath-0.1.11.js"></script>
+        <script language="JavaScript" type="text/javascript" src="lib/resemble.js"></script>
         <script language="JavaScript" type="text/javascript" src="scripts/user-extensions.js"></script>
 </head>
 
diff --git a/product/Zelenium/selenium/core/TestRunner.html b/product/Zelenium/selenium/core/TestRunner.html
index f11cdf4ce1..78d5ed2c5f 100644
--- a/product/Zelenium/selenium/core/TestRunner.html
+++ b/product/Zelenium/selenium/core/TestRunner.html
@@ -48,6 +48,7 @@ to work-around a bug in IE on Win2K whereby the HTA application doesn't function
         <script language="JavaScript" type="text/javascript" src="xpath/dom.js"></script>
         <script language="JavaScript" type="text/javascript" src="xpath/xpath.js"></script>
         <script language="JavaScript" type="text/javascript" src="xpath/javascript-xpath-0.1.11.js"></script>
+        <script language="JavaScript" type="text/javascript" src="lib/resemble.js"></script>
         <script language="JavaScript" type="text/javascript" src="scripts/user-extensions.js"></script>
 </head>
 
diff --git a/product/Zelenium/selenium/core/lib/resemble.js b/product/Zelenium/selenium/core/lib/resemble.js
new file mode 100644
index 0000000000..a3a71e3918
--- /dev/null
+++ b/product/Zelenium/selenium/core/lib/resemble.js
@@ -0,0 +1,1032 @@
+/*
+James Cryer / Huddle
+URL: https://github.com/Huddle/Resemble.js
+*/
+
+(function(root, factory) {
+    "use strict";
+    if (typeof define === "function" && define.amd) {
+        define([], factory);
+    } else if (typeof module === "object" && module.exports) {
+        module.exports = factory();
+    } else {
+        root.resemble = factory();
+    }
+})(this, function() {
+    "use strict";
+
+    var Img;
+    var Canvas;
+
+    if (typeof Image !== "undefined") {
+        Img = Image;
+    } else {
+        Canvas = require("canvas-prebuilt"); // eslint-disable-line global-require
+        Img = Canvas.Image;
+    }
+
+    var document =
+        typeof window !== "undefined"
+            ? window.document
+            : {
+                  createElement: function() {
+                      // This will work as long as only createElement is used on window.document
+                      return new Canvas();
+                  }
+              };
+
+    var oldGlobalSettings = {};
+    var globalOutputSettings = oldGlobalSettings;
+
+    function setGlobalOutputSettings(settings) {
+        var msg =
+            "warning resemble.outputSettings mutates global state, and " +
+            "will be removed in 3.0.0";
+        console.warn(msg);
+        globalOutputSettings = settings;
+        return this;
+    }
+
+    var resemble = function(fileData) {
+        var pixelTransparency = 1;
+
+        var errorPixelColor = {
+            // Color for Error Pixels. Between 0 and 255.
+            red: 255,
+            green: 0,
+            blue: 255,
+            alpha: 255
+        };
+
+        var targetPix = { r: 0, g: 0, b: 0, a: 0 }; // isAntialiased
+
+        function colorsDistance(c1, c2) {
+            return (
+                (Math.abs(c1.r - c2.r) +
+                    Math.abs(c1.g - c2.g) +
+                    Math.abs(c1.b - c2.b)) /
+                3
+            );
+        }
+
+        function withinBoundingBox(x, y, width, height, box) {
+            return (
+                x > (box.left || 0) &&
+                x < (box.right || width) &&
+                y > (box.top || 0) &&
+                y < (box.bottom || height)
+            );
+        }
+
+        function withinComparedArea(x, y, width, height) {
+            var isIncluded = true;
+
+            if (
+                boundingBox !== undefined &&
+                !withinBoundingBox(x, y, width, height, boundingBox)
+            ) {
+                isIncluded = false;
+            }
+
+            if (
+                ignoredBox !== undefined &&
+                withinBoundingBox(x, y, width, height, ignoredBox)
+            ) {
+                isIncluded = false;
+            }
+
+            return isIncluded;
+        }
+
+        var errorPixelTransform = {
+            flat: function(px, offset) {
+                px[offset] = errorPixelColor.red;
+                px[offset + 1] = errorPixelColor.green;
+                px[offset + 2] = errorPixelColor.blue;
+                px[offset + 3] = errorPixelColor.alpha;
+            },
+            movement: function(px, offset, d1, d2) {
+                px[offset] =
+                    (d2.r * (errorPixelColor.red / 255) + errorPixelColor.red) /
+                    2;
+                px[offset + 1] =
+                    (d2.g * (errorPixelColor.green / 255) +
+                        errorPixelColor.green) /
+                    2;
+                px[offset + 2] =
+                    (d2.b * (errorPixelColor.blue / 255) +
+                        errorPixelColor.blue) /
+                    2;
+                px[offset + 3] = d2.a;
+            },
+            flatDifferenceIntensity: function(px, offset, d1, d2) {
+                px[offset] = errorPixelColor.red;
+                px[offset + 1] = errorPixelColor.green;
+                px[offset + 2] = errorPixelColor.blue;
+                px[offset + 3] = colorsDistance(d1, d2);
+            },
+            movementDifferenceIntensity: function(px, offset, d1, d2) {
+                var ratio = colorsDistance(d1, d2) / 255 * 0.8;
+
+                px[offset] =
+                    (1 - ratio) * (d2.r * (errorPixelColor.red / 255)) +
+                    ratio * errorPixelColor.red;
+                px[offset + 1] =
+                    (1 - ratio) * (d2.g * (errorPixelColor.green / 255)) +
+                    ratio * errorPixelColor.green;
+                px[offset + 2] =
+                    (1 - ratio) * (d2.b * (errorPixelColor.blue / 255)) +
+                    ratio * errorPixelColor.blue;
+                px[offset + 3] = d2.a;
+            },
+            diffOnly: function(px, offset, d1, d2) {
+                px[offset] = d2.r;
+                px[offset + 1] = d2.g;
+                px[offset + 2] = d2.b;
+                px[offset + 3] = d2.a;
+            }
+        };
+
+        var errorPixel = errorPixelTransform.flat;
+        var errorType;
+        var boundingBox;
+        var ignoredBox;
+        var largeImageThreshold = 1200;
+        var useCrossOrigin = true;
+        var data = {};
+        var images = [];
+        var updateCallbackArray = [];
+
+        var tolerance = {
+            // between 0 and 255
+            red: 16,
+            green: 16,
+            blue: 16,
+            alpha: 16,
+            minBrightness: 16,
+            maxBrightness: 240
+        };
+
+        var ignoreAntialiasing = false;
+        var ignoreColors = false;
+        var scaleToSameSize = false;
+
+        function triggerDataUpdate() {
+            var len = updateCallbackArray.length;
+            var i;
+            for (i = 0; i < len; i++) {
+                if (typeof updateCallbackArray[i] === "function") {
+                    updateCallbackArray[i](data);
+                }
+            }
+        }
+
+        function loop(w, h, callback) {
+            var x;
+            var y;
+
+            for (x = 0; x < w; x++) {
+                for (y = 0; y < h; y++) {
+                    callback(x, y);
+                }
+            }
+        }
+
+        function parseImage(sourceImageData, width, height) {
+            var pixelCount = 0;
+            var redTotal = 0;
+            var greenTotal = 0;
+            var blueTotal = 0;
+            var alphaTotal = 0;
+            var brightnessTotal = 0;
+            var whiteTotal = 0;
+            var blackTotal = 0;
+
+            loop(width, height, function(horizontalPos, verticalPos) {
+                var offset = (verticalPos * width + horizontalPos) * 4;
+                var red = sourceImageData[offset];
+                var green = sourceImageData[offset + 1];
+                var blue = sourceImageData[offset + 2];
+                var alpha = sourceImageData[offset + 3];
+                var brightness = getBrightness(red, green, blue);
+
+                if (red === green && red === blue && alpha) {
+                    if (red === 0) {
+                        blackTotal++;
+                    } else if (red === 255) {
+                        whiteTotal++;
+                    }
+                }
+
+                pixelCount++;
+
+                redTotal += red / 255 * 100;
+                greenTotal += green / 255 * 100;
+                blueTotal += blue / 255 * 100;
+                alphaTotal += (255 - alpha) / 255 * 100;
+                brightnessTotal += brightness / 255 * 100;
+            });
+
+            data.red = Math.floor(redTotal / pixelCount);
+            data.green = Math.floor(greenTotal / pixelCount);
+            data.blue = Math.floor(blueTotal / pixelCount);
+            data.alpha = Math.floor(alphaTotal / pixelCount);
+            data.brightness = Math.floor(brightnessTotal / pixelCount);
+            data.white = Math.floor(whiteTotal / pixelCount * 100);
+            data.black = Math.floor(blackTotal / pixelCount * 100);
+
+            triggerDataUpdate();
+        }
+
+        function loadImageData(fileDataForImage, callback) {
+            var fileReader;
+            var hiddenImage = new Img();
+
+            if (!hiddenImage.setAttribute) {
+                hiddenImage.setAttribute = function setAttribute() {};
+            }
+
+            if (useCrossOrigin) {
+                hiddenImage.setAttribute("crossorigin", "anonymous");
+            }
+
+            hiddenImage.onerror = function(err) {
+                hiddenImage.onload = null;
+                hiddenImage.onerror = null; // fixes pollution between calls
+                images.push({ error: err ? err + "" : "Image load error." });
+                callback();
+            };
+
+            hiddenImage.onload = function() {
+                hiddenImage.onload = null; // fixes pollution between calls
+                hiddenImage.onerror = null;
+
+                var hiddenCanvas = document.createElement("canvas");
+                var imageData;
+
+                // don't assign to hiddenImage, see https://github.com/Huddle/Resemble.js/pull/87/commits/300d43352a2845aad289b254bfbdc7cd6a37e2d7
+                var width = hiddenImage.width;
+                var height = hiddenImage.height;
+
+                if (scaleToSameSize && images.length === 1) {
+                    width = images[0].width;
+                    height = images[0].height;
+                }
+
+                hiddenCanvas.width = width;
+                hiddenCanvas.height = height;
+
+                hiddenCanvas
+                    .getContext("2d")
+                    .drawImage(hiddenImage, 0, 0, width, height);
+                imageData = hiddenCanvas
+                    .getContext("2d")
+                    .getImageData(0, 0, width, height);
+
+                images.push(imageData);
+
+                callback(imageData, width, height);
+            };
+
+            if (typeof fileDataForImage === "string") {
+                hiddenImage.src = fileDataForImage;
+                if (hiddenImage.complete && hiddenImage.naturalWidth > 0) {
+                    hiddenImage.onload();
+                }
+            } else if (
+                typeof fileDataForImage.data !== "undefined" &&
+                typeof fileDataForImage.width === "number" &&
+                typeof fileDataForImage.height === "number"
+            ) {
+                images.push(fileDataForImage);
+
+                callback(
+                    fileDataForImage,
+                    fileDataForImage.width,
+                    fileDataForImage.height
+                );
+            } else if (
+                typeof Buffer !== "undefined" &&
+                fileDataForImage instanceof Buffer
+            ) {
+                // If we have Buffer, assume we're on Node+Canvas and its supported
+                hiddenImage.src = fileDataForImage;
+            } else {
+                fileReader = new FileReader();
+                fileReader.onload = function(event) {
+                    hiddenImage.src = event.target.result;
+                };
+                fileReader.readAsDataURL(fileDataForImage);
+            }
+        }
+
+        function isColorSimilar(a, b, color) {
+            var absDiff = Math.abs(a - b);
+
+            if (typeof a === "undefined") {
+                return false;
+            }
+            if (typeof b === "undefined") {
+                return false;
+            }
+
+            if (a === b) {
+                return true;
+            } else if (absDiff < tolerance[color]) {
+                return true;
+            }
+            return false;
+        }
+
+        function isPixelBrightnessSimilar(d1, d2) {
+            var alpha = isColorSimilar(d1.a, d2.a, "alpha");
+            var brightness = isColorSimilar(
+                d1.brightness,
+                d2.brightness,
+                "minBrightness"
+            );
+            return brightness && alpha;
+        }
+
+        function getBrightness(r, g, b) {
+            return 0.3 * r + 0.59 * g + 0.11 * b;
+        }
+
+        function isRGBSame(d1, d2) {
+            var red = d1.r === d2.r;
+            var green = d1.g === d2.g;
+            var blue = d1.b === d2.b;
+            return red && green && blue;
+        }
+
+        function isRGBSimilar(d1, d2) {
+            var red = isColorSimilar(d1.r, d2.r, "red");
+            var green = isColorSimilar(d1.g, d2.g, "green");
+            var blue = isColorSimilar(d1.b, d2.b, "blue");
+            var alpha = isColorSimilar(d1.a, d2.a, "alpha");
+
+            return red && green && blue && alpha;
+        }
+
+        function isContrasting(d1, d2) {
+            return (
+                Math.abs(d1.brightness - d2.brightness) >
+                tolerance.maxBrightness
+            );
+        }
+
+        function getHue(red, green, blue) {
+            var r = red / 255;
+            var g = green / 255;
+            var b = blue / 255;
+            var max = Math.max(r, g, b);
+            var min = Math.min(r, g, b);
+            var h;
+            var d;
+
+            if (max === min) {
+                h = 0; // achromatic
+            } else {
+                d = max - min;
+                switch (max) {
+                    case r:
+                        h = (g - b) / d + (g < b ? 6 : 0);
+                        break;
+                    case g:
+                        h = (b - r) / d + 2;
+                        break;
+                    case b:
+                        h = (r - g) / d + 4;
+                        break;
+                    default:
+                        h /= 6;
+                }
+            }
+
+            return h;
+        }
+
+        function isAntialiased(
+            sourcePix,
+            pix,
+            cacheSet,
+            verticalPos,
+            horizontalPos,
+            width
+        ) {
+            var offset;
+            var distance = 1;
+            var i;
+            var j;
+            var hasHighContrastSibling = 0;
+            var hasSiblingWithDifferentHue = 0;
+            var hasEquivalentSibling = 0;
+
+            addHueInfo(sourcePix);
+
+            for (i = distance * -1; i <= distance; i++) {
+                for (j = distance * -1; j <= distance; j++) {
+                    if (i === 0 && j === 0) {
+                        // ignore source pixel
+                    } else {
+                        offset =
+                            ((verticalPos + j) * width + (horizontalPos + i)) *
+                            4;
+
+                        if (!getPixelInfo(targetPix, pix, offset, cacheSet)) {
+                            continue;
+                        }
+
+                        addBrightnessInfo(targetPix);
+                        addHueInfo(targetPix);
+
+                        if (isContrasting(sourcePix, targetPix)) {
+                            hasHighContrastSibling++;
+                        }
+
+                        if (isRGBSame(sourcePix, targetPix)) {
+                            hasEquivalentSibling++;
+                        }
+
+                        if (Math.abs(targetPix.h - sourcePix.h) > 0.3) {
+                            hasSiblingWithDifferentHue++;
+                        }
+
+                        if (
+                            hasSiblingWithDifferentHue > 1 ||
+                            hasHighContrastSibling > 1
+                        ) {
+                            return true;
+                        }
+                    }
+                }
+            }
+
+            if (hasEquivalentSibling < 2) {
+                return true;
+            }
+
+            return false;
+        }
+
+        function copyPixel(px, offset, pix) {
+            if (errorType === "diffOnly") {
+                return;
+            }
+
+            px[offset] = pix.r; // r
+            px[offset + 1] = pix.g; // g
+            px[offset + 2] = pix.b; // b
+            px[offset + 3] = pix.a * pixelTransparency; // a
+        }
+
+        function copyGrayScalePixel(px, offset, pix) {
+            if (errorType === "diffOnly") {
+                return;
+            }
+
+            px[offset] = pix.brightness; // r
+            px[offset + 1] = pix.brightness; // g
+            px[offset + 2] = pix.brightness; // b
+            px[offset + 3] = pix.a * pixelTransparency; // a
+        }
+
+        function getPixelInfo(dst, pix, offset) {
+            if (pix.length > offset) {
+                dst.r = pix[offset];
+                dst.g = pix[offset + 1];
+                dst.b = pix[offset + 2];
+                dst.a = pix[offset + 3];
+
+                return true;
+            }
+
+            return false;
+        }
+
+        function addBrightnessInfo(pix) {
+            pix.brightness = getBrightness(pix.r, pix.g, pix.b); // 'corrected' lightness
+        }
+
+        function addHueInfo(pix) {
+            pix.h = getHue(pix.r, pix.g, pix.b);
+        }
+
+        function analyseImages(img1, img2, width, height) {
+            var hiddenCanvas = document.createElement("canvas");
+
+            var data1 = img1.data;
+            var data2 = img2.data;
+
+            hiddenCanvas.width = width;
+            hiddenCanvas.height = height;
+
+            var context = hiddenCanvas.getContext("2d");
+            var imgd = context.createImageData(width, height);
+            var pix = imgd.data;
+
+            var mismatchCount = 0;
+            var diffBounds = {
+                top: height,
+                left: width,
+                bottom: 0,
+                right: 0
+            };
+            var updateBounds = function(x, y) {
+                diffBounds.left = Math.min(x, diffBounds.left);
+                diffBounds.right = Math.max(x, diffBounds.right);
+                diffBounds.top = Math.min(y, diffBounds.top);
+                diffBounds.bottom = Math.max(y, diffBounds.bottom);
+            };
+
+            var time = Date.now();
+
+            var skip;
+
+            if (
+                !!largeImageThreshold &&
+                ignoreAntialiasing &&
+                (width > largeImageThreshold || height > largeImageThreshold)
+            ) {
+                skip = 6;
+            }
+
+            var pixel1 = { r: 0, g: 0, b: 0, a: 0 };
+            var pixel2 = { r: 0, g: 0, b: 0, a: 0 };
+
+            loop(width, height, function(horizontalPos, verticalPos) {
+                if (skip) {
+                    // only skip if the image isn't small
+                    if (
+                        verticalPos % skip === 0 ||
+                        horizontalPos % skip === 0
+                    ) {
+                        return;
+                    }
+                }
+
+                var offset = (verticalPos * width + horizontalPos) * 4;
+                var isWithinComparedArea = withinComparedArea(
+                    horizontalPos,
+                    verticalPos,
+                    width,
+                    height
+                );
+
+                if (
+                    !getPixelInfo(pixel1, data1, offset, 1) ||
+                    !getPixelInfo(pixel2, data2, offset, 2)
+                ) {
+                    return;
+                }
+
+                if (ignoreColors) {
+                    addBrightnessInfo(pixel1);
+                    addBrightnessInfo(pixel2);
+
+                    if (
+                        isPixelBrightnessSimilar(pixel1, pixel2) ||
+                        !isWithinComparedArea
+                    ) {
+                        copyGrayScalePixel(pix, offset, pixel2);
+                    } else {
+                        errorPixel(pix, offset, pixel1, pixel2);
+                        mismatchCount++;
+                        updateBounds(horizontalPos, verticalPos);
+                    }
+                    return;
+                }
+
+                if (isRGBSimilar(pixel1, pixel2) || !isWithinComparedArea) {
+                    copyPixel(pix, offset, pixel1);
+                } else if (
+                    ignoreAntialiasing &&
+                    (addBrightnessInfo(pixel1), // jit pixel info augmentation looks a little weird, sorry.
+                    addBrightnessInfo(pixel2),
+                    isAntialiased(
+                        pixel1,
+                        data1,
+                        1,
+                        verticalPos,
+                        horizontalPos,
+                        width
+                    ) ||
+                        isAntialiased(
+                            pixel2,
+                            data2,
+                            2,
+                            verticalPos,
+                            horizontalPos,
+                            width
+                        ))
+                ) {
+                    if (
+                        isPixelBrightnessSimilar(pixel1, pixel2) ||
+                        !isWithinComparedArea
+                    ) {
+                        copyGrayScalePixel(pix, offset, pixel2);
+                    } else {
+                        errorPixel(pix, offset, pixel1, pixel2);
+                        mismatchCount++;
+                        updateBounds(horizontalPos, verticalPos);
+                    }
+                } else {
+                    errorPixel(pix, offset, pixel1, pixel2);
+                    mismatchCount++;
+                    updateBounds(horizontalPos, verticalPos);
+                }
+            });
+
+            data.rawMisMatchPercentage = mismatchCount / (height * width) * 100;
+            data.misMatchPercentage = data.rawMisMatchPercentage.toFixed(2);
+            data.diffBounds = diffBounds;
+            data.analysisTime = Date.now() - time;
+
+            data.getImageDataUrl = function(text) {
+                var barHeight = 0;
+
+                if (text) {
+                    barHeight = addLabel(text, context, hiddenCanvas);
+                }
+
+                context.putImageData(imgd, 0, barHeight);
+
+                return hiddenCanvas.toDataURL("image/png");
+            };
+
+            if (hiddenCanvas.toBuffer) {
+                data.getBuffer = function(includeOriginal) {
+                    if (includeOriginal) {
+                        var imageWidth = hiddenCanvas.width + 2;
+                        hiddenCanvas.width = imageWidth * 3;
+                        context.putImageData(img1, 0, 0);
+                        context.putImageData(img2, imageWidth, 0);
+                        context.putImageData(imgd, imageWidth * 2, 0);
+                    } else {
+                        context.putImageData(imgd, 0, 0);
+                    }
+                    return hiddenCanvas.toBuffer();
+                };
+            }
+        }
+
+        function addLabel(text, context, hiddenCanvas) {
+            var textPadding = 2;
+
+            context.font = "12px sans-serif";
+
+            var textWidth = context.measureText(text).width + textPadding * 2;
+            var barHeight = 22;
+
+            if (textWidth > hiddenCanvas.width) {
+                hiddenCanvas.width = textWidth;
+            }
+
+            hiddenCanvas.height += barHeight;
+
+            context.fillStyle = "#666";
+            context.fillRect(0, 0, hiddenCanvas.width, barHeight - 4);
+            context.fillStyle = "#fff";
+            context.fillRect(0, barHeight - 4, hiddenCanvas.width, 4);
+
+            context.fillStyle = "#fff";
+            context.textBaseline = "top";
+            context.font = "12px sans-serif";
+            context.fillText(text, textPadding, 1);
+
+            return barHeight;
+        }
+
+        function normalise(img, w, h) {
+            var c;
+            var context;
+
+            if (img.height < h || img.width < w) {
+                c = document.createElement("canvas");
+                c.width = w;
+                c.height = h;
+                context = c.getContext("2d");
+                context.putImageData(img, 0, 0);
+                return context.getImageData(0, 0, w, h);
+            }
+
+            return img;
+        }
+
+        function outputSettings(options) {
+            var key;
+
+            if (options.errorColor) {
+                for (key in options.errorColor) {
+                    if (options.errorColor.hasOwnProperty(key)) {
+                        errorPixelColor[key] =
+                            options.errorColor[key] === void 0
+                                ? errorPixelColor[key]
+                                : options.errorColor[key];
+                    }
+                }
+            }
+
+            if (options.errorType && errorPixelTransform[options.errorType]) {
+                errorPixel = errorPixelTransform[options.errorType];
+                errorType = options.errorType;
+            }
+
+            if (
+                options.errorPixel &&
+                typeof options.errorPixel === "function"
+            ) {
+                errorPixel = options.errorPixel;
+            }
+
+            pixelTransparency = isNaN(Number(options.transparency))
+                ? pixelTransparency
+                : options.transparency;
+
+            if (options.largeImageThreshold !== undefined) {
+                largeImageThreshold = options.largeImageThreshold;
+            }
+
+            if (options.useCrossOrigin !== undefined) {
+                useCrossOrigin = options.useCrossOrigin;
+            }
+
+            if (options.boundingBox !== undefined) {
+                boundingBox = options.boundingBox;
+            }
+
+            if (options.ignoredBox !== undefined) {
+                ignoredBox = options.ignoredBox;
+            }
+        }
+
+        function compare(one, two) {
+            if (globalOutputSettings !== oldGlobalSettings) {
+                outputSettings(globalOutputSettings);
+            }
+
+            function onceWeHaveBoth() {
+                var width;
+                var height;
+                if (images.length === 2) {
+                    if (images[0].error || images[1].error) {
+                        data = {};
+                        data.error = images[0].error
+                            ? images[0].error
+                            : images[1].error;
+                        triggerDataUpdate();
+                        return;
+                    }
+                    width =
+                        images[0].width > images[1].width
+                            ? images[0].width
+                            : images[1].width;
+                    height =
+                        images[0].height > images[1].height
+                            ? images[0].height
+                            : images[1].height;
+
+                    if (
+                        images[0].width === images[1].width &&
+                        images[0].height === images[1].height
+                    ) {
+                        data.isSameDimensions = true;
+                    } else {
+                        data.isSameDimensions = false;
+                    }
+
+                    data.dimensionDifference = {
+                        width: images[0].width - images[1].width,
+                        height: images[0].height - images[1].height
+                    };
+
+                    analyseImages(
+                        normalise(images[0], width, height),
+                        normalise(images[1], width, height),
+                        width,
+                        height
+                    );
+
+                    triggerDataUpdate();
+                }
+            }
+
+            images = [];
+            loadImageData(one, onceWeHaveBoth);
+            loadImageData(two, onceWeHaveBoth);
+        }
+
+        function getCompareApi(param) {
+            var secondFileData;
+            var hasMethod = typeof param === "function";
+
+            if (!hasMethod) {
+                // assume it's file data
+                secondFileData = param;
+            }
+
+            var self = {
+                scaleToSameSize: function() {
+                    scaleToSameSize = true;
+
+                    if (hasMethod) {
+                        param();
+                    }
+                    return self;
+                },
+                useOriginalSize: function() {
+                    scaleToSameSize = false;
+
+                    if (hasMethod) {
+                        param();
+                    }
+                    return self;
+                },
+                ignoreNothing: function() {
+                    tolerance.red = 0;
+                    tolerance.green = 0;
+                    tolerance.blue = 0;
+                    tolerance.alpha = 0;
+                    tolerance.minBrightness = 0;
+                    tolerance.maxBrightness = 255;
+
+                    ignoreAntialiasing = false;
+                    ignoreColors = false;
+
+                    if (hasMethod) {
+                        param();
+                    }
+                    return self;
+                },
+                ignoreLess: function() {
+                    tolerance.red = 16;
+                    tolerance.green = 16;
+                    tolerance.blue = 16;
+                    tolerance.alpha = 16;
+                    tolerance.minBrightness = 16;
+                    tolerance.maxBrightness = 240;
+
+                    ignoreAntialiasing = false;
+                    ignoreColors = false;
+
+                    if (hasMethod) {
+                        param();
+                    }
+                    return self;
+                },
+                ignoreAntialiasing: function() {
+                    tolerance.red = 32;
+                    tolerance.green = 32;
+                    tolerance.blue = 32;
+                    tolerance.alpha = 32;
+                    tolerance.minBrightness = 64;
+                    tolerance.maxBrightness = 96;
+
+                    ignoreAntialiasing = true;
+                    ignoreColors = false;
+
+                    if (hasMethod) {
+                        param();
+                    }
+                    return self;
+                },
+                ignoreColors: function() {
+                    tolerance.alpha = 16;
+                    tolerance.minBrightness = 16;
+                    tolerance.maxBrightness = 240;
+
+                    ignoreAntialiasing = false;
+                    ignoreColors = true;
+
+                    if (hasMethod) {
+                        param();
+                    }
+                    return self;
+                },
+                ignoreAlpha: function() {
+                    tolerance.red = 16;
+                    tolerance.green = 16;
+                    tolerance.blue = 16;
+                    tolerance.alpha = 255;
+                    tolerance.minBrightness = 16;
+                    tolerance.maxBrightness = 240;
+
+                    ignoreAntialiasing = false;
+                    ignoreColors = false;
+
+                    if (hasMethod) {
+                        param();
+                    }
+                    return self;
+                },
+                repaint: function() {
+                    if (hasMethod) {
+                        param();
+                    }
+                    return self;
+                },
+                outputSettings: function(options) {
+                    outputSettings(options);
+                    return self;
+                },
+                onComplete: function(callback) {
+                    updateCallbackArray.push(callback);
+
+                    var wrapper = function() {
+                        compare(fileData, secondFileData);
+                    };
+
+                    wrapper();
+
+                    return getCompareApi(wrapper);
+                }
+            };
+
+            return self;
+        }
+
+        var rootSelf = {
+            onComplete: function(callback) {
+                updateCallbackArray.push(callback);
+                loadImageData(fileData, function(imageData, width, height) {
+                    parseImage(imageData.data, width, height);
+                });
+            },
+            compareTo: function(secondFileData) {
+                return getCompareApi(secondFileData);
+            },
+            outputSettings: function(options) {
+                outputSettings(options);
+                return rootSelf;
+            }
+        };
+        return rootSelf;
+    };
+
+    function applyIgnore(api, ignore) {
+        switch (ignore) {
+            case "nothing":
+                api.ignoreNothing();
+                break;
+            case "less":
+                api.ignoreLess();
+                break;
+            case "antialiasing":
+                api.ignoreAntialiasing();
+                break;
+            case "colors":
+                api.ignoreColors();
+                break;
+            case "alpha":
+                api.ignoreAlpha();
+                break;
+            default:
+                throw new Error("Invalid ignore: " + ignore);
+        }
+    }
+
+    resemble.compare = function(image1, image2, options, cb) {
+        var callback;
+        var opt;
+
+        if (typeof options === "function") {
+            callback = options;
+            opt = {};
+        } else {
+            callback = cb;
+            opt = options || {};
+        }
+
+        var res = resemble(image1);
+        var compare;
+
+        if (opt.output) {
+            res.outputSettings(opt.output);
+        }
+
+        compare = res.compareTo(image2);
+
+        if (opt.scaleToSameSize) {
+            compare.scaleToSameSize();
+        }
+
+        if (typeof opt.ignore === "string") {
+            applyIgnore(compare, opt.ignore);
+        } else if (opt.ignore && opt.ignore.forEach) {
+            opt.ignore.forEach(function(v) {
+                applyIgnore(compare, v);
+            });
+        }
+
+        compare.onComplete(function(data) {
+            if (data.error) {
+                callback(data.error);
+            } else {
+                callback(null, data);
+            }
+        });
+    };
+
+    resemble.outputSettings = setGlobalOutputSettings;
+    return resemble;
+});
-- 
2.30.9