fix equalOrEmpty for empty strings
This commit is contained in:
parent
5e5fecbcfe
commit
bc8d8b235c
|
@ -1,7 +1,18 @@
|
||||||
/* global styleSectionsEqual prefs download tryJSONparse ignoreChromeError
|
/* global
|
||||||
calcStyleDigest getStyleWithNoCode debounce chromeLocal
|
API_METHODS
|
||||||
usercss semverCompare styleJSONseemsValid
|
calcStyleDigest
|
||||||
API_METHODS styleManager */
|
chromeLocal
|
||||||
|
debounce
|
||||||
|
download
|
||||||
|
getStyleWithNoCode
|
||||||
|
ignoreChromeError
|
||||||
|
prefs
|
||||||
|
semverCompare
|
||||||
|
styleJSONseemsValid
|
||||||
|
styleManager
|
||||||
|
tryJSONparse
|
||||||
|
usercss
|
||||||
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
(() => {
|
(() => {
|
||||||
|
@ -208,7 +219,7 @@
|
||||||
delete json.enabled;
|
delete json.enabled;
|
||||||
|
|
||||||
const newStyle = Object.assign({}, style, json);
|
const newStyle = Object.assign({}, style, json);
|
||||||
if (styleSectionsEqual(json, style, {checkSource: true})) {
|
if (json.sourceCode === style.sourceCode) {
|
||||||
// update digest even if save === false as there might be just a space added etc.
|
// update digest even if save === false as there might be just a space added etc.
|
||||||
return styleManager.installStyle(newStyle)
|
return styleManager.installStyle(newStyle)
|
||||||
.then(saved => {
|
.then(saved => {
|
||||||
|
|
|
@ -264,40 +264,31 @@
|
||||||
.catch(() => null);
|
.catch(() => null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The sections are checked in successive order because it matters when many sections
|
||||||
|
* match the same URL and they have rules with the same CSS specificity
|
||||||
|
* @param {Object} a - first style object
|
||||||
|
* @param {Object} b - second style object
|
||||||
|
* @returns {?boolean}
|
||||||
|
*/
|
||||||
function styleSectionsEqual({sections: a}, {sections: b}) {
|
function styleSectionsEqual({sections: a}, {sections: b}) {
|
||||||
if (!a || !b) {
|
const targets = ['urls', 'urlPrefixes', 'domains', 'regexps'];
|
||||||
return undefined;
|
return a && b && a.length === b.length && a.every(sameSection);
|
||||||
|
function sameSection(secA, i) {
|
||||||
|
return equalOrEmpty(secA.code, b[i].code, 'string', (a, b) => a === b) &&
|
||||||
|
targets.every(target => equalOrEmpty(secA[target], b[i][target], 'array', arrayMirrors));
|
||||||
}
|
}
|
||||||
if (a.length !== b.length) {
|
function equalOrEmpty(a, b, type, comparator) {
|
||||||
return false;
|
const typeA = type === 'array' ? Array.isArray(a) : typeof a === type;
|
||||||
|
const typeB = type === 'array' ? Array.isArray(b) : typeof b === type;
|
||||||
|
return typeA && typeB && comparator(a, b) ||
|
||||||
|
(a == null || typeA && !a.length) &&
|
||||||
|
(b == null || typeB && !b.length);
|
||||||
}
|
}
|
||||||
// order of sections should be identical to account for the case of multiple
|
function arrayMirrors(a, b) {
|
||||||
// sections matching the same URL because the order of rules is part of cascading
|
return a.length === b.length &&
|
||||||
return a.every((sectionA, index) => propertiesEqual(sectionA, b[index]));
|
a.every(el => b.includes(el)) &&
|
||||||
|
b.every(el => a.includes(el));
|
||||||
function propertiesEqual(secA, secB) {
|
|
||||||
for (const name of ['urlPrefixes', 'urls', 'domains', 'regexps']) {
|
|
||||||
if (!equalOrEmpty(secA[name], secB[name], 'every', arrayMirrors)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return equalOrEmpty(secA.code, secB.code, 'substr', (a, b) => a === b);
|
|
||||||
}
|
|
||||||
|
|
||||||
function equalOrEmpty(a, b, telltale, comparator) {
|
|
||||||
const typeA = a && typeof a[telltale] === 'function';
|
|
||||||
const typeB = b && typeof b[telltale] === 'function';
|
|
||||||
return (
|
|
||||||
(a === null || a === undefined || (typeA && !a.length)) &&
|
|
||||||
(b === null || b === undefined || (typeB && !b.length))
|
|
||||||
) || typeA && typeB && a.length === b.length && comparator(a, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
function arrayMirrors(array1, array2) {
|
|
||||||
return (
|
|
||||||
array1.every(el => array2.includes(el)) &&
|
|
||||||
array2.every(el => array1.includes(el))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,57 +23,30 @@ function styleSectionGlobal(section) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Style} a - first style object
|
* The sections are checked in successive order because it matters when many sections
|
||||||
* @param {Style} b - second style object
|
* match the same URL and they have rules with the same CSS specificity
|
||||||
* @param {Object} options
|
* @param {Object} a - first style object
|
||||||
* @param {Boolean=} options.ignoreCode -
|
* @param {Object} b - second style object
|
||||||
* true used by invalidateCache to determine if cached filters should be cleared
|
* @returns {?boolean}
|
||||||
* @param {Boolean=} options.checkSource -
|
|
||||||
* true used by update check to compare the server response
|
|
||||||
* instead of sections that depend on @preprocessor
|
|
||||||
* @returns {Boolean|undefined}
|
|
||||||
*/
|
*/
|
||||||
function styleSectionsEqual(a, b, {ignoreCode, checkSource} = {}) {
|
function styleSectionsEqual({sections: a}, {sections: b}) {
|
||||||
if (checkSource &&
|
const targets = ['urls', 'urlPrefixes', 'domains', 'regexps'];
|
||||||
typeof a.sourceCode === 'string' &&
|
return a && b && a.length === b.length && a.every(sameSection);
|
||||||
typeof b.sourceCode === 'string') {
|
function sameSection(secA, i) {
|
||||||
return a.sourceCode === b.sourceCode;
|
return equalOrEmpty(secA.code, b[i].code, 'string', (a, b) => a === b) &&
|
||||||
|
targets.every(target => equalOrEmpty(secA[target], b[i][target], 'array', arrayMirrors));
|
||||||
}
|
}
|
||||||
a = a.sections;
|
function equalOrEmpty(a, b, type, comparator) {
|
||||||
b = b.sections;
|
const typeA = type === 'array' ? Array.isArray(a) : typeof a === type;
|
||||||
if (!a || !b) {
|
const typeB = type === 'array' ? Array.isArray(b) : typeof b === type;
|
||||||
return undefined;
|
return typeA && typeB && comparator(a, b) ||
|
||||||
|
(a == null || typeA && !a.length) &&
|
||||||
|
(b == null || typeB && !b.length);
|
||||||
}
|
}
|
||||||
if (a.length !== b.length) {
|
function arrayMirrors(a, b) {
|
||||||
return false;
|
return a.length === b.length &&
|
||||||
}
|
a.every(el => b.includes(el)) &&
|
||||||
// order of sections should be identical to account for the case of multiple
|
b.every(el => a.includes(el));
|
||||||
// sections matching the same URL because the order of rules is part of cascading
|
|
||||||
return a.every((sectionA, index) => propertiesEqual(sectionA, b[index]));
|
|
||||||
|
|
||||||
function propertiesEqual(secA, secB) {
|
|
||||||
for (const name of ['urlPrefixes', 'urls', 'domains', 'regexps']) {
|
|
||||||
if (!equalOrEmpty(secA[name], secB[name], 'every', arrayMirrors)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ignoreCode || equalOrEmpty(secA.code, secB.code, 'substr', (a, b) => a === b);
|
|
||||||
}
|
|
||||||
|
|
||||||
function equalOrEmpty(a, b, telltale, comparator) {
|
|
||||||
const typeA = a && typeof a[telltale] === 'function';
|
|
||||||
const typeB = b && typeof b[telltale] === 'function';
|
|
||||||
return (
|
|
||||||
(a === null || a === undefined || (typeA && !a.length)) &&
|
|
||||||
(b === null || b === undefined || (typeB && !b.length))
|
|
||||||
) || typeA && typeB && a.length === b.length && comparator(a, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
function arrayMirrors(array1, array2) {
|
|
||||||
return (
|
|
||||||
array1.every(el => array2.includes(el)) &&
|
|
||||||
array2.every(el => array1.includes(el))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user