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