From 59f5d477f6d328dca614acd822f51f6523407061 Mon Sep 17 00:00:00 2001 From: Vladimir Mijic Date: Wed, 4 Jan 2023 12:31:08 +0100 Subject: [PATCH 1/5] optimization to render built in styles for specific class attributes --- .../to/etc/domui/dom/HtmlTagRenderer.java | 46 +++++++++++++++++-- .../resources/themes/scss/winter/_core.scss | 16 +++++++ 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/to.etc.domui/src/main/java/to/etc/domui/dom/HtmlTagRenderer.java b/to.etc.domui/src/main/java/to/etc/domui/dom/HtmlTagRenderer.java index 78da7064de..b1ea9b0902 100644 --- a/to.etc.domui/src/main/java/to/etc/domui/dom/HtmlTagRenderer.java +++ b/to.etc.domui/src/main/java/to/etc/domui/dom/HtmlTagRenderer.java @@ -81,7 +81,10 @@ import to.etc.util.StringTool; import java.io.IOException; +import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; /** * Basic, mostly standard-compliant handler for rendering HTML tags. @@ -90,6 +93,15 @@ * Created on Aug 17, 2007 */ public class HtmlTagRenderer implements INodeVisitor { + + /** Replacing specific style attributes with build-in classes -> optimization when we render styles attributes as jquery js code for CSP support */ + private static final Map STYLE_ATTRIBUTES_TO_CLASSES = Map.of( + "display:none", "ui-display-none", // + "display:inline", "ui-display-inline", // + "display:block", "ui-display-block", // + "display:flex", "ui-display-flex" // + ); + /** Scratch stringbuffer. */ private StringBuilder m_sb; @@ -678,8 +690,24 @@ public void basicNodeRender(final NodeBase b, final IBrowserOutput o, boolean in } String s = getStyleFor(b); // Get/recalculate style + + //collect replacing classes for specified style attributes + List classesFromStyleAttributes = new ArrayList<>(); if(!s.isEmpty() || b.isStyleRendered()) { - o.attr("style", s); + StringBuilder styleSb = new StringBuilder(); + String[] styles = s.split(";"); + for(int i = 0; i < styles.length; i++) { + String stylePart = styles[i]; + String aClass = STYLE_ATTRIBUTES_TO_CLASSES.get(stylePart); + if(null != aClass) { + classesFromStyleAttributes.add(aClass); + }else { + styleSb.append(stylePart).append(";"); + } + } + if(styleSb.length() > 0) { + o.attr("style", styleSb.toString()); + } if(s.isEmpty()) { b.clearStyleRendered(); // if we rendered the empty style we can remove it next time } else { @@ -721,8 +749,20 @@ public void basicNodeRender(final NodeBase b, final IBrowserOutput o, boolean in if(b.isStretchHeight()) o.attr("stretch", "true"); - if(b.getCssClass() != null) - o.attr("class", b.getCssClass()); + + //append collected replacing classes too to existing classes of an element + String cssClass = b.getCssClass(); + if(cssClass != null || !classesFromStyleAttributes.isEmpty()) { + if(!classesFromStyleAttributes.isEmpty()) { + String appendedClasses = classesFromStyleAttributes.stream().collect(Collectors.joining(" ")); + if(null == cssClass) { + cssClass = appendedClasses; + }else { + cssClass = cssClass + " " + appendedClasses; + } + } + o.attr("class", cssClass); + } List sal = b.getSpecialAttributeList(); if(sal != null) { diff --git a/to.etc.domui/src/main/resources/resources/themes/scss/winter/_core.scss b/to.etc.domui/src/main/resources/resources/themes/scss/winter/_core.scss index d97bc68794..3e798ad42b 100644 --- a/to.etc.domui/src/main/resources/resources/themes/scss/winter/_core.scss +++ b/to.etc.domui/src/main/resources/resources/themes/scss/winter/_core.scss @@ -209,3 +209,19 @@ i, em { text-align: left; white-space: nowrap; } + +.ui-display-none { + display: none !important; +} + +.ui-display-inline { + display: inline !important; +} + +.ui-display-block { + display: block !important; +} + +.ui-display-flex { + display: flex !important; +} From 20a24e4d49226feebc44b1492e2ad32044a18e90 Mon Sep 17 00:00:00 2001 From: Vladimir Mijic Date: Wed, 4 Jan 2023 12:34:29 +0100 Subject: [PATCH 2/5] skipping blank parts --- .../to/etc/domui/dom/HtmlTagRenderer.java | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/to.etc.domui/src/main/java/to/etc/domui/dom/HtmlTagRenderer.java b/to.etc.domui/src/main/java/to/etc/domui/dom/HtmlTagRenderer.java index b1ea9b0902..75a3b9ff20 100644 --- a/to.etc.domui/src/main/java/to/etc/domui/dom/HtmlTagRenderer.java +++ b/to.etc.domui/src/main/java/to/etc/domui/dom/HtmlTagRenderer.java @@ -86,6 +86,8 @@ import java.util.Map; import java.util.stream.Collectors; +import static to.etc.util.StringTool.isBlank; + /** * Basic, mostly standard-compliant handler for rendering HTML tags. * @@ -627,7 +629,7 @@ static private String border(final StringBuilder a, final int w, final String s, } static private void renderBorderIf(@NonNull Appendable a, @Nullable String border) throws IOException { - if(StringTool.isBlank(border)) + if(isBlank(border)) return; a.append("border:").append(border).append(";"); } @@ -698,11 +700,13 @@ public void basicNodeRender(final NodeBase b, final IBrowserOutput o, boolean in String[] styles = s.split(";"); for(int i = 0; i < styles.length; i++) { String stylePart = styles[i]; - String aClass = STYLE_ATTRIBUTES_TO_CLASSES.get(stylePart); - if(null != aClass) { - classesFromStyleAttributes.add(aClass); - }else { - styleSb.append(stylePart).append(";"); + if(!isBlank(stylePart)) { + String aClass = STYLE_ATTRIBUTES_TO_CLASSES.get(stylePart); + if(null != aClass) { + classesFromStyleAttributes.add(aClass); + } else { + styleSb.append(stylePart).append(";"); + } } } if(styleSb.length() > 0) { @@ -1116,11 +1120,11 @@ public void visitInput(final Input n) throws Exception { o().attr("onblur", sb().append(transformScript).append("WebUI.hideLookupTypingPopup('").append(n.getActualID()).append("')").toString()); } else { //-- Attach normal onKeyPress handling. - if(!StringTool.isBlank(n.getOnKeyPressJS())) { + if(!isBlank(n.getOnKeyPressJS())) { o().attr("onkeypress", "return " + n.getOnKeyPressJS()); } - if(!StringTool.isBlank(transformScript)) { + if(!isBlank(transformScript)) { o().attr("onblur", sb().append(transformScript).toString()); } } From e586985e94b6b586a62310033dc40f4ce3f38989 Mon Sep 17 00:00:00 2001 From: Vladimir Mijic Date: Wed, 4 Jan 2023 17:24:15 +0100 Subject: [PATCH 3/5] Fixed slideUp and slideDown to use display styles --- .../java/to/etc/domui/dom/Animations.java | 7 +- .../resources/themes/scss/winter/_core.scss | 8 +- .../resources/resources/ts/domui.jquery.d.ts | 1 + .../resources/resources/ts/domui.jquery.ts | 74 +++++++++++++++++++ 4 files changed, 83 insertions(+), 7 deletions(-) diff --git a/to.etc.domui/src/main/java/to/etc/domui/dom/Animations.java b/to.etc.domui/src/main/java/to/etc/domui/dom/Animations.java index f5a71d27c5..bfef5fe347 100644 --- a/to.etc.domui/src/main/java/to/etc/domui/dom/Animations.java +++ b/to.etc.domui/src/main/java/to/etc/domui/dom/Animations.java @@ -48,14 +48,14 @@ public NodeFixer(NodeBase node, DisplayType newDisplay) { * Since after the slideDown the node in the browser will have its default display this is also the state * in DomUI's node. * - * @param node + * @param node target node */ static public void slideDown(NodeBase node) { node.getPage().addAfterRenderListener(new NodeFixer(node, node.getDisplay())); node.setDisplay(DisplayType.NONE); node.appendStatement() .select(node) - .method("slideDown").end() + .method("doSlideDown").end() .endmethod() .next(); } @@ -75,7 +75,7 @@ static public void slideUp(NodeBase node, String jsCallback) { node.getPage().addAfterRenderListener(new NodeFixer(node, DisplayType.NONE)); node.appendStatement() .select(node) - .append(".slideUp(function() { " + jsCallback + " })") + .append(".doSlideUp(function() { " + jsCallback + " })") .next(); } @@ -104,6 +104,7 @@ static public void bounce(NodeBase node) { /** * Does pulsate effect. + * * @param node target node * @param times if 0 it uses default behavior for pulsate. */ diff --git a/to.etc.domui/src/main/resources/resources/themes/scss/winter/_core.scss b/to.etc.domui/src/main/resources/resources/themes/scss/winter/_core.scss index 3e798ad42b..1312d40607 100644 --- a/to.etc.domui/src/main/resources/resources/themes/scss/winter/_core.scss +++ b/to.etc.domui/src/main/resources/resources/themes/scss/winter/_core.scss @@ -211,17 +211,17 @@ i, em { } .ui-display-none { - display: none !important; + display: none; } .ui-display-inline { - display: inline !important; + display: inline; } .ui-display-block { - display: block !important; + display: block; } .ui-display-flex { - display: flex !important; + display: flex; } diff --git a/to.etc.domui/src/main/resources/resources/ts/domui.jquery.d.ts b/to.etc.domui/src/main/resources/resources/ts/domui.jquery.d.ts index 4dd68e8edd..2dc3b736fc 100644 --- a/to.etc.domui/src/main/resources/resources/ts/domui.jquery.d.ts +++ b/to.etc.domui/src/main/resources/resources/ts/domui.jquery.d.ts @@ -9,6 +9,7 @@ interface JQuery { doStretch() : void; fixOverflow() : void; + fixDisplayClass() : void; setBackgroundImageMarker() : void; size() : number; diff --git a/to.etc.domui/src/main/resources/resources/ts/domui.jquery.ts b/to.etc.domui/src/main/resources/resources/ts/domui.jquery.ts index 85e04c3389..a388972b5f 100644 --- a/to.etc.domui/src/main/resources/resources/ts/domui.jquery.ts +++ b/to.etc.domui/src/main/resources/resources/ts/domui.jquery.ts @@ -153,6 +153,80 @@ $.fn.extend({ }); $(this).markerTransformed = true; }); + }, + + fixDisplayClass: function(): void { + let style = this.attr("style"); + if(null == style) { + return; + } + if(style.includes("display: none;")) { + style = style + .replace("display: none;", "") + this.attr("style", style); + this.removeClass("ui-display-block"); + this.removeClass("ui-display-flex"); + this.removeClass("ui-display-inline"); + this.addClass("ui-display-none"); + } + if(style.includes("display: block;")) { + style = style + .replace("display: block;", "") + this.attr("style", style); + this.removeClass("ui-display-none"); + this.removeClass("ui-display-flex"); + this.removeClass("ui-display-inline"); + this.addClass("ui-display-block"); + } + if(style.includes("display: inline;")) { + style = style + .replace("display: inline;", "") + this.attr("style", style); + this.removeClass("ui-display-none"); + this.removeClass("ui-display-block"); + this.removeClass("ui-display-flex"); + this.addClass("ui-display-inline"); + } + if(style.includes("display: flex;")) { + style = style + .replace("display: flex;", "") + this.attr("style", style); + this.removeClass("ui-display-none"); + this.removeClass("ui-display-block"); + this.removeClass("ui-display-inline"); + this.addClass("ui-display-flex"); + } + }, + + doSlideUp: function(callback) { + let fixDisplayCallback = function(e) { + $(e).fixDisplayClass(); + } + + let myOnSlideUp = fixDisplayCallback; + if(null != callback) { + myOnSlideUp = function() { + callback(); + fixDisplayCallback(this); + } + } + this.slideUp(myOnSlideUp); + }, + + doSlideDown: function(callback) { + let fixDisplayCallback = function(e) { + $(e).fixDisplayClass(); + } + + let myOnSlideDown = fixDisplayCallback; + if(null != callback) { + myOnSlideDown = function() { + callback(); + fixDisplayCallback(this); + } + } + this.slideDown(myOnSlideDown); } + }); From fa5c0a4d636e4ed116db5274514a7b2cc8ae8d54 Mon Sep 17 00:00:00 2001 From: Vladimir Mijic Date: Thu, 5 Jan 2023 13:40:33 +0100 Subject: [PATCH 4/5] Fixes priority of ui-display-XXX styles. Fixes SCP header definition, script-src works in both FF and Chrome (previously defined default-src worked only in Chrome). --- .../resources/resources/themes/scss/winter/_core.scss | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/to.etc.domui/src/main/resources/resources/themes/scss/winter/_core.scss b/to.etc.domui/src/main/resources/resources/themes/scss/winter/_core.scss index 1312d40607..3e798ad42b 100644 --- a/to.etc.domui/src/main/resources/resources/themes/scss/winter/_core.scss +++ b/to.etc.domui/src/main/resources/resources/themes/scss/winter/_core.scss @@ -211,17 +211,17 @@ i, em { } .ui-display-none { - display: none; + display: none !important; } .ui-display-inline { - display: inline; + display: inline !important; } .ui-display-block { - display: block; + display: block !important; } .ui-display-flex { - display: flex; + display: flex !important; } From a650a79b3d22d69dc272c95a869c3f4fd3f08279 Mon Sep 17 00:00:00 2001 From: Vladimir Mijic Date: Thu, 5 Jan 2023 17:19:26 +0100 Subject: [PATCH 5/5] Fixes fadeIn effect for flare messages. --- .../main/resources/resources/ts/domui.comp.ts | 52 +++++++++++++++ .../resources/resources/ts/domui.jquery.d.ts | 1 - .../resources/resources/ts/domui.jquery.ts | 63 +++---------------- 3 files changed, 62 insertions(+), 54 deletions(-) diff --git a/to.etc.domui/src/main/resources/resources/ts/domui.comp.ts b/to.etc.domui/src/main/resources/resources/ts/domui.comp.ts index a1f4761f94..b0638026d8 100644 --- a/to.etc.domui/src/main/resources/resources/ts/domui.comp.ts +++ b/to.etc.domui/src/main/resources/resources/ts/domui.comp.ts @@ -393,8 +393,54 @@ namespace WebUI { WebUI.valuechanged('eh', id); } + export function fixDisplayClass(elem): void { + let style = $(elem).attr("style"); + if(null == style) { + return; + } + if(style.includes("display: none;")) { + style = style + .replace("display: none;", "") + $(elem).attr("style", style); + $(elem).removeClass("ui-display-block"); + $(elem).removeClass("ui-display-flex"); + $(elem).removeClass("ui-display-inline"); + $(elem).addClass("ui-display-none"); + } + if(style.includes("display: block;")) { + style = style + .replace("display: block;", "") + $(elem).attr("style", style); + $(elem).removeClass("ui-display-none"); + $(elem).removeClass("ui-display-flex"); + $(elem).removeClass("ui-display-inline"); + $(elem).addClass("ui-display-block"); + } + if(style.includes("display: inline;")) { + style = style + .replace("display: inline;", "") + $(elem).attr("style", style); + $(elem).removeClass("ui-display-none"); + $(elem).removeClass("ui-display-block"); + $(elem).removeClass("ui-display-flex"); + $(elem).addClass("ui-display-inline"); + } + if(style.includes("display: flex;")) { + style = style + .replace("display: flex;", "") + $(elem).attr("style", style); + $(elem).removeClass("ui-display-none"); + $(elem).removeClass("ui-display-block"); + $(elem).removeClass("ui-display-inline"); + $(elem).addClass("ui-display-flex"); + } + } + export function flare(id): void { + $('#' + id).css('display', 'none'); + $('#' + id).removeClass("ui-display-none"); $('#' + id).fadeIn('fast', function() { + WebUI.fixDisplayClass($('#' + id)); $('#' + id).delay(500).fadeOut(1000, function() { $('#' + id).remove(); }); @@ -402,7 +448,10 @@ namespace WebUI { } export function flareStay(id): void { + $('#' + id).css('display', 'none'); + $('#' + id).removeClass("ui-display-none"); $('#' + id).fadeIn('fast', function() { + WebUI.fixDisplayClass($('#' + id)); $('body,html').bind('mousemove.' + id, function(e) { $('body,html').unbind('mousemove.' + id); $('#' + id).delay(500).fadeOut(1000, function() { @@ -413,7 +462,10 @@ namespace WebUI { } export function flareStayCustom(id, delay, fadeOut): void { + $('#' + id).css('display', 'none'); + $('#' + id).removeClass("ui-display-none"); $('#' + id).fadeIn('fast', function() { + WebUI.fixDisplayClass($('#' + id)); $('body,html').bind('mousemove.' + id, function(e) { $('body,html').unbind('mousemove.' + id); $('#' + id).delay(delay).fadeOut(fadeOut, function() { diff --git a/to.etc.domui/src/main/resources/resources/ts/domui.jquery.d.ts b/to.etc.domui/src/main/resources/resources/ts/domui.jquery.d.ts index 2dc3b736fc..4dd68e8edd 100644 --- a/to.etc.domui/src/main/resources/resources/ts/domui.jquery.d.ts +++ b/to.etc.domui/src/main/resources/resources/ts/domui.jquery.d.ts @@ -9,7 +9,6 @@ interface JQuery { doStretch() : void; fixOverflow() : void; - fixDisplayClass() : void; setBackgroundImageMarker() : void; size() : number; diff --git a/to.etc.domui/src/main/resources/resources/ts/domui.jquery.ts b/to.etc.domui/src/main/resources/resources/ts/domui.jquery.ts index a388972b5f..21476b4851 100644 --- a/to.etc.domui/src/main/resources/resources/ts/domui.jquery.ts +++ b/to.etc.domui/src/main/resources/resources/ts/domui.jquery.ts @@ -155,77 +155,34 @@ $.fn.extend({ }); }, - fixDisplayClass: function(): void { - let style = this.attr("style"); - if(null == style) { - return; - } - if(style.includes("display: none;")) { - style = style - .replace("display: none;", "") - this.attr("style", style); - this.removeClass("ui-display-block"); - this.removeClass("ui-display-flex"); - this.removeClass("ui-display-inline"); - this.addClass("ui-display-none"); - } - if(style.includes("display: block;")) { - style = style - .replace("display: block;", "") - this.attr("style", style); - this.removeClass("ui-display-none"); - this.removeClass("ui-display-flex"); - this.removeClass("ui-display-inline"); - this.addClass("ui-display-block"); - } - if(style.includes("display: inline;")) { - style = style - .replace("display: inline;", "") - this.attr("style", style); - this.removeClass("ui-display-none"); - this.removeClass("ui-display-block"); - this.removeClass("ui-display-flex"); - this.addClass("ui-display-inline"); - } - if(style.includes("display: flex;")) { - style = style - .replace("display: flex;", "") - this.attr("style", style); - this.removeClass("ui-display-none"); - this.removeClass("ui-display-block"); - this.removeClass("ui-display-inline"); - this.addClass("ui-display-flex"); - } - }, - doSlideUp: function(callback) { - let fixDisplayCallback = function(e) { - $(e).fixDisplayClass(); + let fixDisplayCallback = function(elem) { + WebUI.fixDisplayClass(elem); } let myOnSlideUp = fixDisplayCallback; if(null != callback) { - myOnSlideUp = function() { + myOnSlideUp = function(elem) { callback(); - fixDisplayCallback(this); + fixDisplayCallback(elem); } } - this.slideUp(myOnSlideUp); + this.slideUp(myOnSlideUp(this)); }, doSlideDown: function(callback) { - let fixDisplayCallback = function(e) { - $(e).fixDisplayClass(); + let fixDisplayCallback = function(elem) { + WebUI.fixDisplayClass(elem); } let myOnSlideDown = fixDisplayCallback; if(null != callback) { - myOnSlideDown = function() { + myOnSlideDown = function(elem) { callback(); - fixDisplayCallback(this); + fixDisplayCallback(elem); } } - this.slideDown(myOnSlideDown); + this.slideDown(myOnSlideDown(this)); } });