Class AppAnalytics

java.lang.Object
com.iizix.jetty.AppAnalytics

public final class AppAnalytics extends Object
Web Analytics for iiziApp.html (CC-ANALYTICS-CONSENT): the one line the servlet injects per response for an application that has a GA4 Measurement ID.

The line is <script>window.izGA={id:"G-...",c:<consent choice>,d:<debug>[,n:"<name>"]};</script>: id the Measurement ID, c the App page's consent choice (1 IIZI asks, 2 the application asks, 0 no prompt), d 1 on a development-mode server (gtag's debug_mode) else 0, and n the application's name in the language of the request (CC-FIX-GA4-V5, GA-6: what the consent prompt names; absent when the application has none, the client then names the appID). The line is written whenever the application has a valid ID, whatever the user's decision, so that the client can start Analytics later without a reload; gtag.js itself is only ever loaded by the client after consent (iz/analytics.js). No ID, no line, no Analytics.

The line is placed by inject(String,String) BEFORE the first external script of the page (CC-FIX-GA4-V5, GA-1): iz/analytics.js is an ordinary script of the page, in the development page by name and in the production page inside its bundle, and it reads window.izGA when it runs - a line after it was never read, and no Browser session ever started Analytics (measured by CC-V5-AUDIT-GA4). The three inline scripts the page starts with, the client gate, the theme first paint and the entry URL hygiene, stay before the line, and so does the client gate's syntax probe iz/gate-syntax.js (GATE_PROBE): an external script that stands right before the gate (CC-CSP-CLIENT-NOEVAL-V5) and is therefore not the first external script this class looks for (CC-FIX-AC3-PREP, GA-D-1).

The user's decision lives on the device only (CC-CLIENT-STORAGE-V5): in the Browser's localStorage, key izr:/:izGA_<appID with '.' as '+'>, read and written by iz/analytics.js. Until version 5 it was a cookie of that name, which the server read on every page request to put a stand-in s into the line; the server reads no cookie any more and the line has no s. The server learns the decision through the environment of the client session (EnvProps.PN_ANALYTICS_CONSENT), never from a request.

Nothing here decides anything about consent: the client shows the prompt and acts on the answer, and a decision is never a condition for any function of the application.

Author:
Christopher Mindus
  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    static final record 
    The per-application settings an IExternalBrowserPages implementation answers for IExternalBrowserPages.getAppAnalytics(String).
  • Method Summary

    Modifier and Type
    Method
    Description
    static String
    Escapes free text for the inside of a double-quoted JavaScript string literal that sits in an inline script of an HTML page: the quote and the backslash by a backslash, and every character that could end the script element or the string early - <, >, &, the control characters and the two Unicode line terminators - as \\uXXXX.
    static int
    Finds the first external script tag of a page: <script with a src attribute, outside HTML comments (<!-- ... -->, the commented-out transform blocks of iiziApp.html among them) and outside the body of an inline script (whose text may well hold a tag in a string).
    static String
    inject(String page, String script)
    Injects the Web Analytics settings line into a served page, BEFORE the first external script of the page (CC-FIX-GA4-V5, GA-1): the first <script> tag with a src attribute that is neither inside an HTML comment nor inside the body of an inline script, nor the client gate's syntax probe (GATE_PROBE, CC-FIX-AC3-PREP, GA-D-1).
    static String
    script(AppAnalytics.Settings settings, boolean debug)
    Builds the line to inject for an application.
  • Method Details

    • script

      public static String script(AppAnalytics.Settings settings, boolean debug)
      Builds the line to inject for an application.
      Parameters:
      settings - The application's settings, or null for an application without Analytics.
      debug - True on a development-mode server: gtag's debug_mode follows it.
      Returns:
      The line, ending in a newline, or null when settings is null or its ID does not match ModuleApp.GA_MEASUREMENT_ID_PATTERN: nothing from a property file reaches the page unchecked, and the pattern admits no quote or backslash. A consent choice outside 0 to 2 is written as 1, IIZI asks. The name, when there is one, is written as n, escaped by escape(String): it is the one member that comes from free text.
    • escape

      public static String escape(String s)
      Escapes free text for the inside of a double-quoted JavaScript string literal that sits in an inline script of an HTML page: the quote and the backslash by a backslash, and every character that could end the script element or the string early - <, >, &, the control characters and the two Unicode line terminators - as \\uXXXX. Everything else, letters of every script included, is written as it is.
      Parameters:
      s - The text, never null.
      Returns:
      The escaped text, without the quotes.
    • inject

      public static String inject(String page, String script)
      Injects the Web Analytics settings line into a served page, BEFORE the first external script of the page (CC-FIX-GA4-V5, GA-1): the first <script> tag with a src attribute that is neither inside an HTML comment nor inside the body of an inline script, nor the client gate's syntax probe (GATE_PROBE, CC-FIX-AC3-PREP, GA-D-1). The inline scripts before it - the client gate, the theme first paint, the entry URL hygiene - stay first, and the module that reads the line, whether listed by name or inside a production bundle, runs after it. When the tag starts a line, the line is inserted before that line with the same indentation. A page without an external script gets the line before </head>, else before </body>, else appended: the rule of CustomDefaultServlet.injectAnalytics, which the Service Worker registration keeps. Applied per response, never to a cached text.
      Parameters:
      page - The page.
      script - The line from script(AppAnalytics.Settings, boolean), ending in a newline, or null for none: the page is then returned as it is.
      Returns:
      The page with the line, or unchanged.
    • firstExternalScript

      public static int firstExternalScript(String page)
      Finds the first external script tag of a page: <script with a src attribute, outside HTML comments (<!-- ... -->, the commented-out transform blocks of iiziApp.html among them) and outside the body of an inline script (whose text may well hold a tag in a string). The client gate's syntax probe, a tag whose text names GATE_PROBE, is passed over as an inline script is: it stands in front of the gate, and the line belongs after the gate (CC-FIX-AC3-PREP, GA-D-1). Since r20714 put the probe there, the line had landed before the probe and so before the gate, the theme first paint and the entry URL hygiene; passing over it puts the line where it stood at r20713.
      Parameters:
      page - The page.
      Returns:
      The index of the tag's <, or -1 when the page has no external script.