You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lo...@apache.org on 2019/07/25 09:19:49 UTC

[myfaces-tobago] branch master updated (b37dba1 -> 4779901)

This is an automated email from the ASF dual-hosted git repository.

lofwyr pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git.


    from b37dba1  TOBAGO-1996 Delete and arrow keys doesn't work properly on tc:date
     new a26ba99  TOBAGO-1633: TS refactoring
     new 744893d  demo
     new 4779901  TOBAGO-1633: TS refactoring: fix Prevent exception, if the last focus id not exists on current page (may happen, when paging in sheet)

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../internal/renderkit/renderer/ImageRenderer.java |   3 +-
 .../internal/renderkit/renderer/SheetRenderer.java |   5 +-
 .../renderkit/renderer/TreeIconRenderer.java       |   3 +-
 .../internal/renderkit/renderer/TreeRenderer.java  |  16 +--
 .../myfaces/tobago/internal/util/JsonUtils.java    |   3 +
 .../myfaces/tobago/internal/util/RenderUtils.java  |   8 +-
 .../myfaces/tobago/internal/util/StringUtils.java  | 120 ++-----------------
 .../webapp/DebugResponseWriterWrapper.java         |   3 +-
 .../myfaces/tobago/example/demo/CategoryTree.java  |   8 +-
 .../20-selector/Sheet_Column_Selector.xhtml        |   5 +-
 .../src/main/npm/ts/tobago-focus.ts                |   5 +-
 .../src/main/npm/ts/tobago-sheet.ts                |  32 ++---
 .../src/main/npm/ts/tobago-tree.ts                 | 132 ++++++++++++---------
 13 files changed, 131 insertions(+), 212 deletions(-)


[myfaces-tobago] 03/03: TOBAGO-1633: TS refactoring: fix Prevent exception, if the last focus id not exists on current page (may happen, when paging in sheet)

Posted by lo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

lofwyr pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git

commit 4779901b76d14ad3397efb7dcae1da3d3f9202e0
Author: Udo Schnurpfeil <lo...@apache.org>
AuthorDate: Thu Jul 25 11:19:37 2019 +0200

    TOBAGO-1633: TS refactoring: fix
    Prevent exception, if the last focus id not exists on current page
    (may happen, when paging in sheet)
---
 tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-focus.ts | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-focus.ts b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-focus.ts
index c406aa5..9e4e075 100644
--- a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-focus.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-focus.ts
@@ -84,7 +84,10 @@ export class Focus {
 
     const lastFocusId = Focus.getLastFocusId();
     if (lastFocusId) {
-      document.getElementById(lastFocusId).focus();
+      const element = document.getElementById(lastFocusId);
+      if (element) {
+        element.focus();
+      }
       return;
     }
 


[myfaces-tobago] 02/03: demo

Posted by lo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

lofwyr pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git

commit 744893d92a11e94e2c2603c078baeac3a5237247
Author: Udo Schnurpfeil <lo...@apache.org>
AuthorDate: Thu Jul 25 11:18:56 2019 +0200

    demo
---
 .../java/org/apache/myfaces/tobago/example/demo/CategoryTree.java | 8 ++++----
 .../080-sheet/20-selector/Sheet_Column_Selector.xhtml             | 5 +++--
 2 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/CategoryTree.java b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/CategoryTree.java
index d2f96c3..d233237 100644
--- a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/CategoryTree.java
+++ b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/CategoryTree.java
@@ -41,10 +41,10 @@ public class CategoryTree {
     final DefaultMutableTreeNode science = createNode("Science", "science");
     science.insert(createNode("Geography", "geography"), 0);
     science.insert(createNode("Mathematics", "math"), 0);
-    final DefaultMutableTreeNode astro = createNode("Astronomy", "astro");
-    astro.insert(createNode("Education", "edu"), 0);
-    astro.insert(createNode("Pictures", "pic"), 0);
-    science.insert(astro, 2);
+    final DefaultMutableTreeNode astronomy = createNode("Astronomy", "astronomy");
+    astronomy.insert(createNode("Education", "edu"), 0);
+    astronomy.insert(createNode("Pictures", "pic"), 0);
+    science.insert(astronomy, 2);
     tree.insert(science, 4);
     return tree;
   }
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/080-sheet/20-selector/Sheet_Column_Selector.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/080-sheet/20-selector/Sheet_Column_Selector.xhtml
index 39b5604..fda20df 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/080-sheet/20-selector/Sheet_Column_Selector.xhtml
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/080-sheet/20-selector/Sheet_Column_Selector.xhtml
@@ -44,7 +44,7 @@
       &lt;tc:out value="\#{solarobject.name}"/>
     &lt;/tc:column>
     ...</code></pre>
-    <tc:sheet id="s4" value="#{sheetController.solarList}" var="object" rows="2"
+    <tc:sheet id="s4" value="#{sheetController.solarList}" var="object" rows="5"
               selectable="#{sheetController.selectable}" state="#{sheetController.sheetState}">
       <tc:columnSelector/>
       <tc:column label="Name">
@@ -70,6 +70,7 @@
     </tc:selectOneRadio>
 
     <tc:button id="b1" label="Submit"/>
-    <tc:out id="o1" label="Row Number" value="#{sheetController.sheetState.selectedRows}"/>
+    <tc:out id="o1" label="Row Number" value="#{sheetController.sheetState.selectedRows}"
+            tip="updated after submit"/>
   </tc:section>
 </ui:composition>


[myfaces-tobago] 01/03: TOBAGO-1633: TS refactoring

Posted by lo...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

lofwyr pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git

commit a26ba992dac5d40e89215fc487cbb54434bfe0e2
Author: Udo Schnurpfeil <lo...@apache.org>
AuthorDate: Thu Jul 25 11:05:13 2019 +0200

    TOBAGO-1633: TS refactoring
    
    TOBAGO-1998: Use JSON for data interchange
    
    sheet: fix row selection position (may be 1 too high)
    
    cleanup
---
 .../internal/renderkit/renderer/ImageRenderer.java |   3 +-
 .../internal/renderkit/renderer/SheetRenderer.java |   5 +-
 .../renderkit/renderer/TreeIconRenderer.java       |   3 +-
 .../internal/renderkit/renderer/TreeRenderer.java  |  16 +--
 .../myfaces/tobago/internal/util/JsonUtils.java    |   3 +
 .../myfaces/tobago/internal/util/RenderUtils.java  |   8 +-
 .../myfaces/tobago/internal/util/StringUtils.java  | 120 ++-----------------
 .../webapp/DebugResponseWriterWrapper.java         |   3 +-
 .../src/main/npm/ts/tobago-sheet.ts                |  32 ++---
 .../src/main/npm/ts/tobago-tree.ts                 | 132 ++++++++++++---------
 10 files changed, 120 insertions(+), 205 deletions(-)

diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/ImageRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/ImageRenderer.java
index 651ca0a..67f7fe0 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/ImageRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/ImageRenderer.java
@@ -24,7 +24,6 @@ import org.apache.myfaces.tobago.internal.component.AbstractUICommandBase;
 import org.apache.myfaces.tobago.internal.component.AbstractUIImage;
 import org.apache.myfaces.tobago.internal.util.HtmlRendererUtils;
 import org.apache.myfaces.tobago.internal.util.JsonUtils;
-import org.apache.myfaces.tobago.internal.util.StringUtils;
 import org.apache.myfaces.tobago.renderkit.RendererBase;
 import org.apache.myfaces.tobago.renderkit.css.BootstrapClass;
 import org.apache.myfaces.tobago.renderkit.css.Icons;
@@ -47,7 +46,7 @@ public class ImageRenderer extends RendererBase {
 
     final AbstractUIImage image = (AbstractUIImage) component;
     final String value = image.getUrl();
-    final boolean fontAwesome = StringUtils.startsWith(value, "fa-");
+    final boolean fontAwesome = value != null && value.startsWith("fa-");
     final boolean disabled = image.isDisabled()
         || (image.getParent() instanceof AbstractUICommandBase
         && ((AbstractUICommandBase) image.getParent()).isDisabled());
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SheetRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SheetRenderer.java
index 4f3ac05..22a1ca9 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SheetRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/SheetRenderer.java
@@ -46,7 +46,6 @@ import org.apache.myfaces.tobago.internal.renderkit.CommandMap;
 import org.apache.myfaces.tobago.internal.util.HtmlRendererUtils;
 import org.apache.myfaces.tobago.internal.util.JsonUtils;
 import org.apache.myfaces.tobago.internal.util.RenderUtils;
-import org.apache.myfaces.tobago.internal.util.StringUtils;
 import org.apache.myfaces.tobago.layout.ShowPosition;
 import org.apache.myfaces.tobago.layout.TextAlign;
 import org.apache.myfaces.tobago.layout.VerticalAlign;
@@ -124,7 +123,7 @@ public class SheetRenderer extends RendererBase {
       }
       List<Integer> selectedRows;
       try {
-        selectedRows = StringUtils.parseIntegerList(selected);
+        selectedRows = JsonUtils.decodeIntegerArray(selected);
       } catch (final NumberFormatException e) {
         LOG.warn(selected, e);
         selectedRows = Collections.emptyList();
@@ -343,7 +342,7 @@ public class SheetRenderer extends RendererBase {
 
     if (selectable != Selectable.none) {
       encodeHiddenInput(writer,
-          StringUtils.joinWithSurroundingSeparator(selectedRows),
+          JsonUtils.encode(selectedRows),
           sheetId + SUFFIX_SELECTED);
     }
 
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeIconRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeIconRenderer.java
index 2d57b2c..f317b3d 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeIconRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeIconRenderer.java
@@ -24,7 +24,6 @@ import org.apache.myfaces.tobago.internal.component.AbstractUITreeIcon;
 import org.apache.myfaces.tobago.internal.component.AbstractUITreeNode;
 import org.apache.myfaces.tobago.internal.util.HtmlRendererUtils;
 import org.apache.myfaces.tobago.internal.util.JsonUtils;
-import org.apache.myfaces.tobago.internal.util.StringUtils;
 import org.apache.myfaces.tobago.renderkit.RendererBase;
 import org.apache.myfaces.tobago.renderkit.css.Icons;
 import org.apache.myfaces.tobago.renderkit.css.TobagoClass;
@@ -97,7 +96,7 @@ public class TreeIconRenderer extends RendererBase {
         TobagoClass.TREE_NODE__TOGGLE,
         treeIcon.getCustomClass());
 
-    if (StringUtils.startsWith(source, "fa-")) {
+    if (source != null && source.startsWith( "fa-")) {
       writer.startElement(HtmlElements.I);
       writer.writeClassAttribute(Icons.FA, Icons.custom(source));
       if (folder) {
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeRenderer.java
index 99d1944..e4e01ff 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TreeRenderer.java
@@ -46,6 +46,8 @@ import javax.faces.component.UIComponent;
 import javax.faces.context.FacesContext;
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
+import java.util.List;
 
 public class TreeRenderer extends RendererBase {
 
@@ -109,10 +111,10 @@ public class TreeRenderer extends RendererBase {
     }
 
     final SelectedState selectedState = tree.getSelectedState();
-    final StringBuilder selectedValue = new StringBuilder(",");
+    final List<Integer> selectedValue = new ArrayList<>();
 
     final ExpandedState expandedState = tree.getExpandedState();
-    final StringBuilder expandedValue = new StringBuilder(",");
+    final List<Integer> expandedValue = new ArrayList<>();
 
     final int last = tree.isRowsUnlimited() ? Integer.MAX_VALUE : tree.getFirst() + tree.getRows();
     for (int rowIndex = tree.getFirst(); rowIndex < last; rowIndex++) {
@@ -124,13 +126,11 @@ public class TreeRenderer extends RendererBase {
       final TreePath path = tree.getPath();
 
       if (selectedState.isSelected(path)) {
-        selectedValue.append(rowIndex);
-        selectedValue.append(",");
+        selectedValue.add(rowIndex);
       }
 
       if (tree.isFolder() && expandedState.isExpanded(path)) {
-        expandedValue.append(rowIndex);
-        expandedValue.append(",");
+        expandedValue.add(rowIndex);
       }
 
       for (final UIComponent child : tree.getChildren()) {
@@ -149,7 +149,7 @@ public class TreeRenderer extends RendererBase {
     writer.writeNameAttribute(selectedId);
     writer.writeIdAttribute(selectedId);
     writer.writeClassAttribute(TobagoClass.TREE__SELECTED);
-    writer.writeAttribute(HtmlAttributes.VALUE, selectedValue.toString(), false);
+    writer.writeAttribute(HtmlAttributes.VALUE, JsonUtils.encode(selectedValue), false);
     writer.endElement(HtmlElements.INPUT);
 
     writer.startElement(HtmlElements.INPUT);
@@ -158,7 +158,7 @@ public class TreeRenderer extends RendererBase {
     writer.writeNameAttribute(expandedId);
     writer.writeIdAttribute(expandedId);
     writer.writeClassAttribute(TobagoClass.TREE__EXPANDED);
-    writer.writeAttribute(HtmlAttributes.VALUE, expandedValue.toString(), false);
+    writer.writeAttribute(HtmlAttributes.VALUE, JsonUtils.encode(expandedValue), false);
     writer.endElement(HtmlElements.INPUT);
 
     writer.startElement(HtmlElements.INPUT);
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/JsonUtils.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/JsonUtils.java
index 4d9d18f..b0726df 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/JsonUtils.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/JsonUtils.java
@@ -324,6 +324,9 @@ public class JsonUtils {
   }
 
   public static List<Integer> decodeIntegerArray(final String json) {
+    if (json == null) {
+      return null;
+    }
     String string = json.trim();
     final List<Integer> result = new ArrayList<>();
     if (string.length() < 2 || string.charAt(0) != '[' || string.charAt(string.length() - 1) != ']') {
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/RenderUtils.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/RenderUtils.java
index 9f25509..95d0df2 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/RenderUtils.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/RenderUtils.java
@@ -225,13 +225,7 @@ public final class RenderUtils {
     final String key = data.getClientId(facesContext) + ComponentUtils.SUB_SEPARATOR + suffix;
     try {
       string = facesContext.getExternalContext().getRequestParameterMap().get(key);
-      if (string != null) {
-        if (string.startsWith("[")) {
-          return JsonUtils.decodeIntegerArray(string);
-        } else {
-          return StringUtils.parseIntegerList(string); // todo remove this case after migrating all to JSON
-        }
-      }
+      return JsonUtils.decodeIntegerArray(string);
     } catch (final Exception e) {
       // should not happen
       LOG.warn("Can't parse " + suffix + ": '" + string + "' from parameter '" + key + "'", e);
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/StringUtils.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/StringUtils.java
index 30ad0e8..5e1baf8 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/StringUtils.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/StringUtils.java
@@ -30,36 +30,6 @@ public final class StringUtils {
     // to prevent instantiation
   }
 
-  public static List<Integer> parseIntegerList(final String integerList) throws NumberFormatException {
-    return parseIntegerList(integerList, ", ;");
-  }
-
-  public static List<Integer> parseIntegerList(final String integerList, final String delimiters)
-      throws NumberFormatException {
-    final List<Integer> list = new ArrayList<>();
-
-    final StringTokenizer tokenizer = new StringTokenizer(integerList, delimiters);
-    while (tokenizer.hasMoreElements()) {
-      final String token = tokenizer.nextToken().trim();
-      if (token.length() > 0) {
-        list.add(new Integer(token));
-      }
-    }
-
-    return list;
-  }
-
-  public static <T> String joinWithSurroundingSeparator(final List<T> list) {
-    final StringBuilder buffer = new StringBuilder(",");
-    if (list != null) {
-      for (final T t : list) {
-        buffer.append(t);
-        buffer.append(",");
-      }
-    }
-    return buffer.toString();
-  }
-
   public static int[] getIndices(final String list) {
     if (list == null) {
       return new int[0];
@@ -313,22 +283,6 @@ public final class StringUtils {
   /**
    * Basically taken from commons-lang
    */
-  public static boolean isAlpha(final String string) {
-    if (string == null) {
-      return false;
-    }
-    final int sz = string.length();
-    for (int i = 0; i < sz; i++) {
-      if (!Character.isLetter(string.charAt(i))) {
-        return false;
-      }
-    }
-    return true;
-  }
-
-  /**
-   * Basically taken from commons-lang
-   */
   public static boolean isEmpty(final String value) {
     return value == null || value.length() == 0;
   }
@@ -367,50 +321,19 @@ public final class StringUtils {
   }
 
   /**
-   * Basically taken from commons-lang
-   */
-  public static String replace(final String text, final String searchString, final String replacement) {
-    if (isEmpty(text) || isEmpty(searchString) || replacement == null) {
-      return text;
-    }
-    int start = 0;
-    int end = text.indexOf(searchString, start);
-    if (end == -1) {
-      return text;
-    }
-    final int replLength = searchString.length();
-    int increase = replacement.length() - replLength;
-    increase = increase < 0 ? 0 : increase * 16;
-    final StringBuilder buf = new StringBuilder(text.length() + increase);
-    while (end != -1) {
-      buf.append(text.substring(start, end)).append(replacement);
-      start = end + replLength;
-      end = text.indexOf(searchString, start);
-    }
-    buf.append(text.substring(start));
-    return buf.toString();
-  }
-
-  /**
-   * Basically taken from commons-lang
-   */
-  public static String repeat(final String str, final int repeat) {
-    final int outputLength = str.length() * repeat;
-    final StringBuilder buf = new StringBuilder(outputLength);
-    for (int i = 0; i < repeat; i++) {
-      buf.append(str);
-    }
-    return buf.toString();
-  }
-
-  /**
-   * Returns a string of the same length to hide confidential passwords from log files etc.
+   * Returns a string with asterisks of the same length to hide confidential passwords from log files etc.
    */
   public static String toConfidentialString(final String string, final boolean confidential) {
     if (string == null) {
       return "<null>";
     } else if (confidential) {
-      return repeat("*", string.length()) + " (confidential)";
+      final int repeat = string.length();
+      final StringBuilder builder = new StringBuilder(repeat + 15);
+      for (int i = 0; i < repeat; i++) {
+        builder.append('*');
+      }
+      builder.append(" (confidential)");
+      return builder.toString();
     } else {
       return string;
     }
@@ -419,26 +342,6 @@ public final class StringUtils {
   /**
    * Basically taken from commons-lang
    */
-  public static String uncapitalize(final String str) {
-    return String.valueOf(Character.toLowerCase(str.charAt(0))) + str.substring(1);
-  }
-
-  /**
-   * Basically taken from commons-lang
-   */
-  public static boolean isAlphanumeric(final String str) {
-    final int sz = str.length();
-    for (int i = 0; i < sz; i++) {
-      if (!Character.isLetterOrDigit(str.charAt(i))) {
-        return false;
-      }
-    }
-    return true;
-  }
-
-  /**
-   * Basically taken from commons-lang
-   */
   public static String join(final List<String> list, final char separator) {
     final int size = list.size();
     if (size <= 0) {
@@ -493,13 +396,6 @@ public final class StringUtils {
     return true;
   }
 
-  public static boolean startsWith(final String string, final String prefix) {
-    if (string == null || prefix == null) {
-      return string == null && prefix == null;
-    }
-    return prefix.length() <= string.length() && string.regionMatches(0, prefix, 0, prefix.length());
-  }
-
   /**
    * <p>
    * Checks if the String contains any character in the given set of characters.
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/webapp/DebugResponseWriterWrapper.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/webapp/DebugResponseWriterWrapper.java
index 56806a6..f7e6df3 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/webapp/DebugResponseWriterWrapper.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/webapp/DebugResponseWriterWrapper.java
@@ -19,7 +19,6 @@
 
 package org.apache.myfaces.tobago.internal.webapp;
 
-import org.apache.myfaces.tobago.internal.util.StringUtils;
 import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
 import org.apache.myfaces.tobago.renderkit.html.HtmlTypes;
 import org.apache.myfaces.tobago.renderkit.html.MarkupLanguageAttributes;
@@ -62,7 +61,7 @@ public class DebugResponseWriterWrapper extends TobagoResponseWriter {
       LOG.error("Comment must not contain the sequence '--', comment = '" + comment + "'.",
           new IllegalArgumentException());
 
-      commentStr = StringUtils.replace(commentStr, "--", "++");
+      commentStr = commentStr.replaceAll("--", "++");
     }
     responseWriter.writeComment(commentStr);
   }
diff --git a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-sheet.ts b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-sheet.ts
index eebe119..a1ec4fe 100644
--- a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-sheet.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-sheet.ts
@@ -402,10 +402,10 @@ class Sheet {
 
       const lastClickedRowIndex = parseInt(sheet.dataset["tobagoLastClickedRowIndex"]);
       if (event.shiftKey && selectionMode === "multi" && lastClickedRowIndex > -1) {
-        if (lastClickedRowIndex <= row.rowIndex) {
-          this.selectRange(rows, lastClickedRowIndex, row.rowIndex, true, false);
+        if (lastClickedRowIndex <= row.sectionRowIndex) {
+          this.selectRange(rows, lastClickedRowIndex, row.sectionRowIndex, true, false);
         } else {
-          this.selectRange(rows, row.rowIndex, lastClickedRowIndex, true, false);
+          this.selectRange(rows, row.sectionRowIndex, lastClickedRowIndex, true, false);
         }
       } else if (selectionMode !== "singleOrNone" || !this.isRowSelected(row)) {
         this.toggleSelection(row, selector);
@@ -517,7 +517,7 @@ class Sheet {
 
   doDblClick(event) {
     const row = <HTMLTableRowElement>event.currentTarget;
-    const rowIndex = row.rowIndex + this.getFirst();
+    const rowIndex = row.sectionRowIndex + this.getFirst();
     if (this.dblClickActionId) {
       let action;
       const index = this.dblClickActionId.indexOf(this.id);
@@ -559,21 +559,22 @@ class Sheet {
   isRowSelected(row: HTMLTableRowElement) {
     let rowIndex = +row.dataset["tobagoRowIndex"];
     if (!rowIndex) {
-      rowIndex = row.rowIndex + this.getFirst();
+      rowIndex = row.sectionRowIndex + this.getFirst();
     }
     return this.isSelected(rowIndex);
   }
 
-  isSelected(rowIndex) {
-    return this.getHiddenSelected().getAttribute("value").indexOf("," + rowIndex + ",") >= 0;
+  isSelected(rowIndex: number) {
+    const value = <number[]>JSON.parse(this.getHiddenSelected().value);
+    return value.indexOf(rowIndex) > -1;
   }
 
   resetSelected() {
-    this.getHiddenSelected().setAttribute("value", ",");
+    this.getHiddenSelected().value = JSON.stringify([]);
   }
 
   toggleSelection(row: HTMLTableRowElement, checkbox: HTMLInputElement) {
-    this.getElement().dataset["tobagoLastClickedRowIndex"] = String(row.rowIndex);
+    this.getElement().dataset["tobagoLastClickedRowIndex"] = String(row.sectionRowIndex);
     if (checkbox && !checkbox.disabled) {
       const selected = this.getHiddenSelected();
       const rowIndex = this.getDataIndex(row);
@@ -603,13 +604,13 @@ class Sheet {
   selectRange(
       rows: NodeListOf<HTMLTableRowElement>, first: number, last: number, selectDeselected: boolean, deselectSelected: boolean) {
     const selected = this.getHiddenSelected();
-    const value = selected.value;
+    const value = new Set<number>(JSON.parse(selected.value));
     for (let i = first; i <= last; i++) {
       const row = rows.item(i);
       const checkbox = this.getSelectorCheckbox(row);
       if (checkbox && !checkbox.disabled) {
         const rowIndex = this.getDataIndex(row);
-        const on = value.indexOf("," + rowIndex + ",") >= 0;
+        const on = value.has(rowIndex);
         if (selectDeselected && !on) {
           this.selectRow(selected, rowIndex, row, checkbox);
         } else if (deselectSelected && on) {
@@ -624,7 +625,7 @@ class Sheet {
     if (rowIndex) {
       return rowIndex;
     } else {
-      return row.rowIndex + this.getFirst();
+      return row.sectionRowIndex + this.getFirst();
     }
   }
 
@@ -635,7 +636,8 @@ class Sheet {
    * @param checkbox input-element: selector in the row.
    */
   selectRow(selected: HTMLInputElement, rowIndex: number, row: HTMLTableRowElement, checkbox: HTMLInputElement) {
-    selected.value = selected.value + rowIndex + ",";
+    const selectedSet = new Set<number>(JSON.parse(selected.value));
+    selected.value = JSON.stringify(Array.from(selectedSet.add(rowIndex)));
     row.classList.add("tobago-sheet-row-markup-selected");
     row.classList.add("table-info");
     checkbox.checked = true;
@@ -651,7 +653,9 @@ class Sheet {
    * @param checkbox input-element: selector in the row.
    */
   deselectRow(selected: HTMLInputElement, rowIndex: number, row: HTMLTableRowElement, checkbox: HTMLInputElement) {
-    selected.value = selected.value.replace(new RegExp("," + rowIndex + ","), ",");
+    const selectedSet = new Set<number>(JSON.parse(selected.value));
+    selectedSet.delete(rowIndex);
+    selected.value = JSON.stringify(Array.from(selectedSet));
     row.classList.remove("tobago-sheet-row-markup-selected");
     row.classList.remove("table-info");
     checkbox.checked = false;
diff --git a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-tree.ts b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-tree.ts
index df1aebc..49cf70d 100644
--- a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-tree.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-tree.ts
@@ -23,11 +23,12 @@ class Tree {
     var src;
     var $node = $element.closest(".tobago-treeNode, .tobago-treeMenuNode");
     var $data = $node.closest(".tobago-treeMenu, .tobago-tree, .tobago-sheet");
-    var $expanded = $data.children(".tobago-treeMenu-expanded, .tobago-tree-expanded, .tobago-sheet-expanded");
+    const expanded: HTMLInputElement
+        = $data.children(".tobago-treeMenu-expanded, .tobago-tree-expanded, .tobago-sheet-expanded").get(0);
     var $toggles = $node.find(".tobago-treeMenuNode-toggle, .tobago-treeNode-toggle");
-    var rowIndex = Tree.rowIndex($node);
-    if (Tree.isExpanded($node, $expanded)) {
-      Tree.hideChildren($node);
+    var rowIndex = Tree.rowIndex($node.get(0));
+    if (Tree.isExpanded($node.get(0), expanded)) {
+      Tree.hideChildren($node.get(0));
       $toggles.find("i").each(function () {
         var $toggle = jQuery(this);
         $toggle.removeClass($toggle.data("tobago-open")).addClass($toggle.data("tobago-closed"));
@@ -40,12 +41,14 @@ class Tree {
         }
         $toggle.attr("src", src);
       });
-      $expanded.val($expanded.val().replace(new RegExp("," + rowIndex + ","), ","));
+      const set = Tree.getSet(expanded);
+      set.delete(rowIndex);
+      Tree.setSet(expanded, set);
       $node.filter(".tobago-treeNode").removeClass("tobago-treeNode-markup-expanded");
       $node.filter(".tobago-treeMenuNode").removeClass("tobago-treeMenuNode-markup-expanded");
     } else {
-      var reload = Tree.showChildren($node, $expanded);
-      $expanded.val($expanded.val() + rowIndex + ",");
+      const reload = Tree.showChildren($node.get(0), expanded);
+      Tree.setSet(expanded, Tree.getSet(expanded).add(rowIndex));
       if (reload) {
         jsf.ajax.request(
             $toggles.parent().attr("id"),
@@ -78,7 +81,7 @@ class Tree {
    * Hide all children of the node recursively.
    * @param node A jQuery-Object as a node of the tree.
    */
-  static hideChildren = function (node) {
+  static hideChildren = function (node: HTMLElement) {
     var inSheet = Tree.isInSheet(node);
     var children = Tree.findChildren(node);
     children.each(function () {
@@ -87,16 +90,17 @@ class Tree {
       } else {
         jQuery(this).hide();
       }
-      Tree.hideChildren(jQuery(this));
+      Tree.hideChildren(this);
     });
   };
 
   /**
    * Show the children of the node recursively, there parents are expanded.
    * @param node A jQuery-Object as a node of the tree.
+   * @param expanded The hidden field which contains the expanded state.
    * @return is reload needed (to get all nodes from the server)
    */
-  static showChildren = function (node, expanded) {
+  static showChildren = function (node: HTMLElement, expanded: HTMLInputElement) {
     var inSheet = Tree.isInSheet(node);
     var children = Tree.findChildren(node);
     if (children.length == 0) {
@@ -104,14 +108,14 @@ class Tree {
       return true;
     }
     children.each(function () {
-      var child = jQuery(this);
+      var $child = jQuery(this);
       if (inSheet) {
-        child.parent().parent().show();
+        $child.parent().parent().show();
       } else {
-        child.show();
+        $child.show();
       }
-      if (Tree.isExpanded(child, expanded)) {
-        var reload = Tree.showChildren(child, expanded);
+      if (Tree.isExpanded($child.get(0), expanded)) {
+        var reload = Tree.showChildren($child.get(0), expanded);
         if (reload) {
           return true;
         }
@@ -146,12 +150,12 @@ class Tree {
     // selected for treeNode
     Tobago4Utils.selectWithJQuery(elements, ".tobago-treeCommand").focus(function () {
       var command = jQuery(this);
-      var node = command.parent(".tobago-treeNode");
-      var tree = node.closest(".tobago-tree");
+      var $node = command.parent(".tobago-treeNode");
+      var tree = $node.closest(".tobago-tree");
       var selected = tree.children(".tobago-tree-selected");
-      selected.val(Tree.rowIndex(node));
+      selected.val(Tree.rowIndex($node.get(0)));
       tree.find(".tobago-treeNode").removeClass("tobago-treeNode-markup-selected");
-      node.addClass("tobago-treeNode-markup-selected");
+      $node.addClass("tobago-treeNode-markup-selected");
     });
 
     // selected for treeSelect
@@ -159,48 +163,58 @@ class Tree {
       var select = jQuery(this);
       var selected = select.prop("checked");
       var data = select.closest(".tobago-treeMenu, .tobago-tree, .tobago-sheet");
-      var hidden = data.children(".tobago-treeMenu-selected, .tobago-tree-selected, .tobago-sheet-selected");
-      var newValue;
+      const hidden: HTMLInputElement
+          = data.children(".tobago-treeMenu-selected, .tobago-tree-selected, .tobago-sheet-selected").get(0);
+      let value: Set<number>;
+      // todo may use an class attribute for this value
       if (select.attr("type") === "radio") {
-        newValue = "," + Tree.rowIndex(select) + ",";
+        value = new Set();
+        value.add(Tree.rowIndex(select.get(0)));
       } else if (selected) {
-        newValue = hidden.val() + Tree.rowIndex(select) + ",";
+        value = Tree.getSet(hidden);
+        value.add(Tree.rowIndex(select.get(0)));
       } else {
-        newValue = (hidden.val() as string).replace(new RegExp("," + Tree.rowIndex(select) + ","), ",");
+        value = Tree.getSet(hidden);
+        value.delete(Tree.rowIndex(select.get(0)));
       }
-      // XXX optimize: regexp and Tobago.Tree.rowIndex
-      hidden.val(newValue);
+      Tree.setSet(hidden, value);
     });
 
     // selected for treeMenuNode
     Tobago4Utils.selectWithJQuery(elements, ".tobago-treeMenuCommand").focus(function () {
       var command = jQuery(this);
-      var node = command.parent(".tobago-treeMenuNode");
-      var tree = node.closest(".tobago-treeMenu");
-      var selected = tree.children(".tobago-treeMenu-selected");
-      selected.val(Tree.rowIndex(node));
+      var $node = command.parent(".tobago-treeMenuNode");
+      var tree = $node.closest(".tobago-treeMenu");
+      const selected: HTMLInputElement = tree.children(".tobago-treeMenu-selected").get(0);
+      const set = new Set<number>();
+      set.add(Tree.rowIndex($node.get(0)));
+      Tree.setSet(selected, set);
       tree.find(".tobago-treeMenuNode").removeClass("tobago-treeMenuNode-markup-selected");
-      node.addClass("tobago-treeMenuNode-markup-selected");
+      $node.addClass("tobago-treeMenuNode-markup-selected");
     });
 
     // init selected field
     Tobago4Utils.selectWithJQuery(elements, ".tobago-treeMenu, .tobago-tree, .tobago-sheet").each(function () {
-      var hidden = jQuery(this).children(".tobago-treeMenu-selected, .tobago-tree-selected, .tobago-sheet-selected");
-      var string = ",";
-      jQuery(this).find(".tobago-treeMenuNode-markup-selected, .tobago-treeNode-markup-selected").each(function () {
-        string += Tree.rowIndex(jQuery(this)) + ",";
-      });
-      hidden.val(string);
+      const hidden: HTMLInputElement = jQuery(this).children(".tobago-treeMenu-selected, .tobago-tree-selected, .tobago-sheet-selected").get(0);
+      if (hidden) {
+        const value = new Set<number>();
+        jQuery(this).find(".tobago-treeMenuNode-markup-selected, .tobago-treeNode-markup-selected").each(function () {
+          value.add(Tree.rowIndex(this));
+        });
+        Tree.setSet(hidden, value);
+      }
     });
 
     // init expanded field
     Tobago4Utils.selectWithJQuery(elements, ".tobago-treeMenu, .tobago-tree, .tobago-sheet").each(function () {
-      var hidden = jQuery(this).children(".tobago-treeMenu-expanded, .tobago-tree-expanded, .tobago-sheet-expanded");
-      var string = ",";
-      jQuery(this).find(".tobago-treeMenuNode-markup-expanded, .tobago-treeNode-markup-expanded").each(function () {
-        string += Tree.rowIndex(jQuery(this)) + ",";
-      });
-      hidden.val(string);
+      const hidden: HTMLInputElement = jQuery(this).children(".tobago-treeMenu-expanded, .tobago-tree-expanded, .tobago-sheet-expanded").get(0);
+      if (hidden) {
+        const value = new Set<number>();
+        jQuery(this).find(".tobago-treeMenuNode-markup-expanded, .tobago-treeNode-markup-expanded").each(function () {
+          value.add(Tree.rowIndex(this));
+        });
+        Tree.setSet(hidden, value);
+      }
     });
 
     // init tree selection for multiCascade
@@ -210,7 +224,7 @@ class Tree {
         jQuery(this).change(function (event) {
           var node = jQuery(event.target).parents(".tobago-treeNode");
           var checked = node.find("input[type=checkbox]").prop("checked");
-          var children = Tree.findChildren(node);
+          var children = Tree.findChildren(node.get(0));
           children.each(function () {
             var childsCheckbox = jQuery(this).find("input[type=checkbox]");
             childsCheckbox.prop("checked", checked);
@@ -222,28 +236,36 @@ class Tree {
 
   };
 
-  static isExpanded = function (node, expanded) {
-    var rowIndex = Tree.rowIndex(node);
-    return expanded.val().indexOf("," + rowIndex + ",") > -1;
+  static getSet(element: HTMLInputElement): Set<number> {
+    return new Set(JSON.parse(element.value));
+  }
+
+  static setSet(element, set: Set<number>) {
+    return element.value = JSON.stringify(Array.from(set));
+  }
+
+  static isExpanded = function (node: HTMLElement, expanded: HTMLInputElement) {
+    const rowIndex = Tree.rowIndex(node);
+    return Tree.getSet(expanded).has(rowIndex);
   };
 
-  static rowIndex = function (node) {
-    return node.attr("id").replace(/.+\:(\d+)(\:\w+)+/, '$1');
+  static rowIndex = function (node: HTMLElement): number { // todo: use attribute data-tobago-row-index
+    return parseInt(node.id.replace(/.+\:(\d+)(\:\w+)+/, '$1'));
   };
 
-  static findChildren = function (node) {
-    var treeParentSelector = "[data-tobago-tree-parent='" + node.attr("id") + "']";
+  static findChildren = function (node: HTMLElement) {
+    var treeParentSelector = "[data-tobago-tree-parent='" + node.id + "']";
     var children;
     if (Tree.isInSheet(node)) {
-      children = node.parent("td").parent("tr").nextAll().children().children(treeParentSelector);
+      children = jQuery(node).parent("td").parent("tr").nextAll().children().children(treeParentSelector);
     } else { // normal tree
-      children = node.nextAll(treeParentSelector);
+      children = jQuery(node).nextAll(treeParentSelector);
     }
     return children;
   };
 
-  static isInSheet = function (node) {
-    return node.parent("td").length === 1;
+  static isInSheet = function (node: HTMLElement) {
+    return jQuery(node).parent("td").length === 1;
   };
 }