Current Dev State

This commit is contained in:
Tim Lorsbach
2025-06-23 20:13:54 +02:00
parent b4f9bb277d
commit ded50edaa2
22617 changed files with 4345095 additions and 174 deletions

18
static/js/ketcher2/node_modules/resemblejs/LICENSE generated vendored Normal file
View File

@ -0,0 +1,18 @@
The MIT License (MIT) Copyright © 2013 Huddle
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the “Software”), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

89
static/js/ketcher2/node_modules/resemblejs/README.md generated vendored Normal file
View File

@ -0,0 +1,89 @@
Resemble.js
==========
Analyse and compare images with Javascript and HTML5. [More info & Resemble.js Demo](http://huddle.github.com/Resemble.js/). If you need NodeJS support, take a look at [node-resemble](https://github.com/ddo/node-resemble) or [node-resemble-v2](https://github.com/peter-mouland/node-resemble-v2).
![Two image diff examples side-by-side, one pink, one yellow.](https://raw.github.com/Huddle/Resemble.js/master/demoassets/readmeimage.jpg "Visual image comparison")
### Get it
`npm install resemblejs`
`bower install resemblejs`
### Example
Retrieve basic analysis on image.
```javascript
var api = resemble(fileData).onComplete(function(data){
console.log(data);
/*
{
red: 255,
green: 255,
blue: 255,
brightness: 255
}
*/
});
```
Use resemble to compare two images.
```javascript
var diff = resemble(file).compareTo(file2).ignoreColors().onComplete(function(data){
console.log(data);
/*
{
misMatchPercentage : 100, // %
isSameDimensions: true, // or false
dimensionDifference: { width: 0, height: -1 }, // defined if dimensions are not the same
getImageDataUrl: function(){}
}
*/
});
```
Scale second image to dimensions of the first one:
```javascript
//diff.useOriginalSize();
diff.scaleToSameSize();
```
You can also change the comparison method after the first analysis.
```javascript
// diff.ignoreNothing();
// diff.ignoreColors();
diff.ignoreAntialiasing();
```
And change the output display style.
```javascript
resemble.outputSettings({
errorColor: {
red: 255,
green: 0,
blue: 255
},
errorType: 'movement',
transparency: 0.3,
largeImageThreshold: 1200,
useCrossOrigin: false
});
// resembleControl.repaint();
```
By default, the comparison algorithm skips pixels when the image width or height is larger than 1200 pixels. This is there to mitigate performance issues.
You can switch this modify this behaviour by setting the `largeImageThreshold` option to a different value. Set it to **0** to switch it off completely.
`useCrossOrigin` is true by default, you might need to set it to false if you're using [Data URIs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs).
--------------------------------------
Created by [James Cryer](http://github.com/jamescryer) and the Huddle development team.

View File

@ -0,0 +1,51 @@
{
"_from": "resemblejs@2.2.3",
"_id": "resemblejs@2.2.3",
"_inBundle": false,
"_integrity": "sha1-LI/KPtOZqXRsfEmA0vyajLG5SmI=",
"_location": "/resemblejs",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "resemblejs@2.2.3",
"name": "resemblejs",
"escapedName": "resemblejs",
"rawSpec": "2.2.3",
"saveSpec": null,
"fetchSpec": "2.2.3"
},
"_requiredBy": [
"/"
],
"_resolved": "https://registry.npmjs.org/resemblejs/-/resemblejs-2.2.3.tgz",
"_shasum": "2c8fca3ed399a9746c7c4980d2fc9a8cb1b94a62",
"_spec": "resemblejs@2.2.3",
"_where": "/home/manfred/enviPath/ketcher2/ketcher",
"author": {
"name": "James Cryer"
},
"bugs": {
"url": "https://github.com/Huddle/Resemble.js/issues"
},
"bundleDependencies": false,
"deprecated": false,
"description": "Image analysis and comparison with HTML5",
"homepage": "https://github.com/Huddle/Resemble.js",
"keywords": [
"comparison",
"visual",
"image",
"diff",
"compare",
"html5"
],
"license": "MIT",
"main": "resemble.js",
"name": "resemblejs",
"repository": {
"type": "git",
"url": "git+https://github.com/Huddle/Resemble.js.git"
},
"version": "2.2.3"
}

707
static/js/ketcher2/node_modules/resemblejs/resemble.js generated vendored Normal file
View File

@ -0,0 +1,707 @@
/*
James Cryer / Huddle 2015
URL: https://github.com/Huddle/Resemble.js
*/
(function (root, factory) {
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 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;
}
var errorPixelTransform = {
flat: function (px, offset, d1, d2) {
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;
}
};
var errorPixel = errorPixelTransform.flat;
var largeImageThreshold = 1200;
var useCrossOrigin = true;
var document = typeof window != "undefined" ? window.document : {};
var resemble = function( fileData ){
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,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( fileData, callback ){
var fileReader;
var hiddenImage = new Image();
if(useCrossOrigin) {
hiddenImage.setAttribute('crossorigin', 'anonymous');
}
hiddenImage.onerror = function () {
hiddenImage.onerror = null; //fixes pollution between calls
images.push({ error : "Image load error."});
callback();
};
hiddenImage.onload = function() {
hiddenImage.onload = null; //fixes pollution between calls
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 fileData === 'string') {
hiddenImage.src = fileData;
if (hiddenImage.complete && hiddenImage.naturalWidth > 0) {
hiddenImage.onload();
}
} else if (typeof fileData.data !== 'undefined'
&& typeof fileData.width === 'number'
&& typeof fileData.height === 'number') {
images.push(fileData);
callback(fileData, fileData.width, fileData.height);
} else {
fileReader = new FileReader();
fileReader.onload = function (event) {
hiddenImage.src = event.target.result;
};
fileReader.readAsDataURL(fileData);
}
}
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;
} else {
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(r,g,b){
r = r / 255;
g = g / 255;
b = b / 255;
var max = Math.max(r, g, b), 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;
}
h /= 6;
}
return h;
}
function isAntialiased(sourcePix, data, 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 , data, 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, data){
px[offset] = data.r; //r
px[offset + 1] = data.g; //g
px[offset + 2] = data.b; //b
px[offset + 3] = data.a * pixelTransparency; //a
}
function copyGrayScalePixel(px, offset, data){
px[offset] = data.brightness; //r
px[offset + 1] = data.brightness; //g
px[offset + 2] = data.brightness; //b
px[offset + 3] = data.a * pixelTransparency; //a
}
function getPixelInfo(dst, data, offset, cacheSet) {
if (data.length > offset) {
dst.r = data[offset];
dst.g = data[offset + 1];
dst.b = data[offset + 2];
dst.a = data[offset + 3];
return true;
}
return false;
}
function addBrightnessInfo(data){
data.brightness = getBrightness(data.r,data.g,data.b); // 'corrected' lightness
}
function addHueInfo(data){
data.h = getHue(data.r,data.g,data.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 targetPix = 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;
if (!getPixelInfo(pixel1, data1, offset, 1) || !getPixelInfo(pixel2, data2, offset, 2)) {
return;
}
if (ignoreColors){
addBrightnessInfo(pixel1);
addBrightnessInfo(pixel2);
if( isPixelBrightnessSimilar(pixel1, pixel2) ){
copyGrayScalePixel(targetPix, offset, pixel2);
} else {
errorPixel(targetPix, offset, pixel1, pixel2);
mismatchCount++;
updateBounds(horizontalPos, verticalPos);
}
return;
}
if( isRGBSimilar(pixel1, pixel2) ){
copyPixel(targetPix, offset, pixel1, pixel2);
} 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) ){
copyGrayScalePixel(targetPix, offset, pixel2);
} else {
errorPixel(targetPix, offset, pixel1, pixel2);
mismatchCount++;
updateBounds(horizontalPos, verticalPos);
}
} else {
errorPixel(targetPix, 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");
};
}
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 compare(one, two){
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,
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;
},
repaint: function(){
if(hasMethod) { param(); }
return self;
},
onComplete: function( callback ){
updateCallbackArray.push(callback);
var wrapper = function(){
compare(fileData, secondFileData);
};
wrapper();
return getCompareApi(wrapper);
}
};
return self;
}
return {
onComplete: function( callback ){
updateCallbackArray.push(callback);
loadImageData(fileData, function(imageData, width, height){
parseImage(imageData.data, width, height);
});
},
compareTo: function(secondFileData){
return getCompareApi(secondFileData);
}
};
};
resemble.outputSettings = function(options){
var key;
var undefined;
if(options.errorColor){
for (key in options.errorColor) {
errorPixelColor[key] = options.errorColor[key] === undefined ? errorPixelColor[key] : options.errorColor[key];
}
}
if(options.errorType && errorPixelTransform[options.errorType] ){
errorPixel = errorPixelTransform[options.errorType];
}
pixelTransparency = isNaN(Number(options.transparency)) ? pixelTransparency : options.transparency;
if (options.largeImageThreshold !== undefined) {
largeImageThreshold = options.largeImageThreshold;
}
if (options.useCrossOrigin !== undefined) {
useCrossOrigin = options.useCrossOrigin;
}
return this;
};
return resemble;
}));

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<p>Check the console for test results</p>
<script src="../resemble.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/chai/3.4.1/chai.js"></script>
<script src="main_spec.js"></script>
</body>
</html>