diff --git a/openslides/motions/static/js/motions/diff.js b/openslides/motions/static/js/motions/diff.js
index 920ec857a..815ca0bfb 100644
--- a/openslides/motions/static/js/motions/diff.js
+++ b/openslides/motions/static/js/motions/diff.js
@@ -625,21 +625,30 @@ angular.module('OpenSlidesApp.motions.diff', ['OpenSlidesApp.motions.lineNumberi
*/
this._normalizeHtmlForDiff = function (html) {
// Convert all HTML tags to uppercase, but leave the values of attributes unchanged
+ // All attributes and CSS class names are sorted alphabetically
html = html.replace(/<(\/?[a-z]*)( [^>]*)?>/ig, function (html, tag, attributes) {
var tagNormalized = tag.toUpperCase();
if (attributes === undefined) {
attributes = "";
}
- attributes = attributes.replace(/( [^"'=]*)(= *((["'])(.*?)\4))?/gi, function (attr, attrName, attrRest, attrRest2, quot, attrValue) {
- var attrNormalized = attrName.toUpperCase();
- if (attrRest !== undefined) {
- if (attrNormalized === ' CLASS') {
- attrValue = attrValue.split(' ').sort().join(' ');
+ var attributesList = [],
+ attributesMatcher = /( [^"'=]*)(= *((["'])(.*?)\4))?/gi,
+ match;
+ do {
+ match = attributesMatcher.exec(attributes);
+ if (match) {
+ var attrNormalized = match[1].toUpperCase(),
+ attrValue = match[5];
+ if (match[2] !== undefined) {
+ if (attrNormalized === ' CLASS') {
+ attrValue = attrValue.split(' ').sort().join(' ');
+ }
+ attrNormalized += "=" + match[4] + attrValue + match[4];
}
- attrNormalized += "=" + quot + attrValue + quot;
+ attributesList.push(attrNormalized);
}
- return attrNormalized;
- });
+ } while (match);
+ attributes = attributesList.sort().join('');
return "<" + tagNormalized + attributes + ">";
});
diff --git a/tests/karma/motions/diff.service.test.js b/tests/karma/motions/diff.service.test.js
index de5e65999..548f1fca8 100644
--- a/tests/karma/motions/diff.service.test.js
+++ b/tests/karma/motions/diff.service.test.js
@@ -5,10 +5,10 @@ describe('linenumbering', function () {
var diffService, baseHtml1, baseHtmlDom1, baseHtml2, baseHtmlDom2, baseHtml3, baseHtmlDom3,
brMarkup = function (no) {
return '
' +
- ' ';
+ ' ';
},
noMarkup = function (no) {
- return ' ';
+ return ' ';
};
beforeEach(inject(function (_diffService_, _lineNumberingService_) {
@@ -374,12 +374,12 @@ describe('linenumbering', function () {
expect(normalized).toBe('The brown fox')
});
- it('uppercases the names of html attributes, but not the values', function () {
+ it('uppercases the names of html attributes, but not the values, and sort the attributes', function () {
var unnormalized = 'This is our cool home page - have a look! ' +
'',
normalized = diffService._normalizeHtmlForDiff(unnormalized);
expect(normalized).toBe('This is our cool home page - have a look! ' +
- '')
+ '')
});
it('strips unnecessary spaces', function () {
@@ -569,6 +569,14 @@ describe('linenumbering', function () {
expect(diff).toBe('
tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren bla, no sea takimata sanctus est Lorem ipsum dolor gubergren sit amet.
sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing
', + after = 'sanctus est Lorem ipsum dolor sit amet. Test Lorem ipsum dolor sit amet, consetetur sadipscing
'; + var diff = diffService.diff(before, after); + + expect(diff).toBe('sanctus est Lorem ipsum dolor sit amet. Test Lorem ipsum dolor sit amet, consetetur sadipscing
'); + }); }); describe('ignoring line numbers', function () {