Example #1
0
 /* validate the syntax of the specified list of multiple email addresses */
 public static boolean validateAddresses(String addrs, boolean acceptSMS) {
   if (StringTools.isBlank(addrs)) {
     // blank is ok in this case
     return true;
   } else if (acceptSMS) {
     // allow "sms:123456789" format
     String addrArry[] = StringTools.parseStringArray(addrs, ',');
     if (addrArry.length == 0) {
       return false;
     }
     for (int i = 0; i < addrArry.length; i++) {
       String em = addrArry[i].trim();
       if (StringTools.isBlank(em)) {
         // individual addresses not allowed
         return false;
       } else if (SMSOutboundGateway.StartsWithSMS(em)) {
         // TODO: for now, accept as-is
       } else if (!validateAddress(em)) {
         return false;
       }
     }
     return true;
   } else {
     // true email addresses only
     try {
       return SendMail.validateAddresses(addrs);
     } catch (Throwable t) { // NoClassDefFoundException, ClassNotFoundException
       // this will fail if JavaMail support for SendMail is not available.
       Print.logError("*** SendMail error: " + t);
       return false;
     }
   }
 }
Example #2
0
  /* write JS to stream */
  public void writeJavaScript(PrintWriter out, RequestProperties reqState) throws IOException {

    /* prefetch map "Loading" image */
    if (this.getProperties().getBoolean(PROP_MAP_LOADING, false)) {
      String mapLoadingImageURI = this.getProperties().getString(PROP_MAP_LOADING_IMAGE, null);
      if (!StringTools.isBlank(mapLoadingImageURI)) {
        out.write("<link rel=\"prefetch\" href=\"" + mapLoadingImageURI + "\">\n");
      }
    }

    /* JSMap variables */
    JavaScriptTools.writeStartJavaScript(out);
    this.writeJSVariables(out, reqState);
    JavaScriptTools.writeEndJavaScript(out);

    /* Subclass JavaScript includes */
    // links to MapProvider support are written by the subclass here
    this.writeJSIncludes(out, reqState);

    /* JSMap Custom included JavaScript */
    String jsMapURLs[] =
        StringTools.parseStringArray(this.getProperties().getString(PROP_javascript_src, ""), '\n');
    this.writeJSIncludes(out, reqState, jsMapURLs);

    /* JSMap Custom inline JavaScript */
    String jsMapInline =
        StringTools.trim(this.getProperties().getString(PROP_javascript_inline, null));
    if (!StringTools.isBlank(jsMapInline)) {
      JavaScriptTools.writeStartJavaScript(out);
      out.write("// --- Inline Javascript [" + this.getName() + "]\n");
      out.write(jsMapInline);
      out.write("\n");
      JavaScriptTools.writeEndJavaScript(out);
    }

    /* event CSV parsing code */
    JavaScriptTools.writeStartJavaScript(out);
    out.write(EventUtil.getInstance().getParseMapEventJS(reqState.isFleet(), reqState.getLocale()));
    JavaScriptTools.writeEndJavaScript(out);
  }
  /** ** Apply conditionals to specified source */
  public static String getConditionalSource(String outputText) {
    // - must have at least one "//#" in this template
    // - This conditional line-inclusion checker is not designed to be a comprehensive
    //   precompiler, but just a simple single-level pre-compile-time template processor.
    // - Will not process conditionals if no 'if' is found
    // - The String " #" is not allow in a specified value (ambiguous with invalid comment)
    // - Does NOT currently support nested IF..ELSE..ENDIF !!!
    // - Prefixing spaces are allowed
    // - If anything other than a spece preceeds the '//#' the conditional will be ignored

    /* exit now if "//#" is not present in template */
    if (outputText.indexOf(COND_) < 0) {
      return outputText;
    }

    /* convert to lines */
    String s[] = StringTools.parseStringArray(outputText, "\n", false); // "\r\n"

    /* loop through code, replacing conditional code */
    StringBuffer sb = new StringBuffer();
    int ifLevel = 0;
    boolean saveLine = true;
    boolean okSave = true;
    checkConditional:
    for (int i = 0; i < s.length; i++) {

      /* current line contains conditional? */
      if (s[i].indexOf(COND_) >= 0) {
        // -- this line contains "//#"
        String S = s[i].trim(); // remove leading space
        // -- remove any trailing comments; ## this is a comment
        int c = S.indexOf(COND_COMMENT);
        if (c > 0) {
          S = S.substring(0, c).trim();
        }
        // -- look for IF..ELSE..ENDIF
        if (S.startsWith(COND_U_IF) || S.startsWith(COND_L_IF)) {
          // //#IF [key[=value]]
          if (ifLevel > 0) {
            // -- already encounted an "if"
            Print.errPrintln("\nNested 'if' encountered");
            okSave = false;
            break checkConditional;
          }
          String cond = S.substring(COND_U_IF.length()).trim();
          int p = cond.indexOf("=");
          String key = (p >= 0) ? cond.substring(0, p).trim() : cond.trim();
          String val = (p >= 0) ? cond.substring(p + 1).trim() : null;
          if (StringTools.isBlank(key)) {
            // //#IF
            // -- no condition, assume "true"
            saveLine = true;
          } else if (val == null) {
            // //#IF [!]key
            // //#IF [!]true
            // //#IF [!]false
            // -- check for prefixing "!"
            boolean not = false;
            if (key.startsWith("!")) {
              not = true;
              key = key.substring(1);
            }
            // -- parse boolean
            boolean bool = false;
            if (key.equalsIgnoreCase("true")) {
              bool = true;
            } else if (key.equalsIgnoreCase("false")) {
              bool = false;
            } else {
              bool = RTConfig.getBoolean(key, false);
            }
            // -- save line?
            saveLine = not ? !bool : bool;
          } else {
            // //#IF key=value
            // -- compare key=value (case insensitive)
            // -- compare key==value (case sensitive)
            String rtpVal = RTConfig.getString(key, "").trim();
            if (val.indexOf(" #") >= 0) {
              Print.errPrintln("\nValue contains invalid comment specification: " + val);
              okSave = false;
              break checkConditional;
            }
            if (val.startsWith("=")) {
              // -- case-sensitive compare
              saveLine = val.substring(1).equals(rtpVal);
            } else {
              // -- case-insensitive compare
              saveLine = val.equalsIgnoreCase(rtpVal);
            }
          }
          ifLevel++;
          continue;
        } else if (S.startsWith(COND_U_ELSE) || S.startsWith(COND_L_ELSE)) {
          // //#ELSE
          if (ifLevel <= 0) {
            // -- found an 'ELSE' without a previous 'IF'
            Print.errPrintln("\n'ELSE' without 'IF' encountered");
            okSave = false;
            break checkConditional;
          }
          saveLine = !saveLine;
          continue;
        } else if (S.startsWith(COND_U_ENDIF) || S.startsWith(COND_L_ENDIF)) {
          // //#ENDIF
          if (ifLevel <= 0) {
            // -- found an 'ENDIF' without a previous 'IF'
            Print.errPrintln("\n'ENDIF' without 'IF' encountered");
            okSave = false;
            break checkConditional;
          }
          ifLevel--;
          saveLine = true;
          continue;
        } else if (S.startsWith(COND_)) {
          // //#???
          Print.errPrintln("\nUnrecognized conditional: " + S);
          continue;
        } else {
          // -- embedded '//#'
        }
      }

      /* save current line? */
      if (saveLine) {
        sb.append(s[i]).append("\n");
      }
    }

    /* save if no errors */
    if (okSave) {
      outputText = sb.toString();
    } else {
      Print.errPrintln("\nConditional code ignored due to previous errors");
    }
    return outputText;
  }
Example #4
0
  /* write mapping support JS to stream */
  protected void writeJSVariables(PrintWriter out, RequestProperties reqState) throws IOException {
    // This var initilizations must not use any functions defined in 'jsmap.js'
    PrivateLabel privLabel = reqState.getPrivateLabel();
    I18N i18n = privLabel.getI18N(JSMap.class);
    Locale locale = reqState.getLocale();
    GeoPoint dftCenter = this.getDefaultCenter(null);
    boolean isFleet = reqState.isFleet();
    Account account = reqState.getCurrentAccount();
    long maxPushpins = this.getMaxPushpins(reqState);
    out.write("// --- Map support Javascript [" + this.getName() + "]\n");
    JavaScriptTools.writeJSVar(out, "MAP_PROVIDER_NAME", this.getName());

    /* properties */
    boolean wrotePropHeader = false;
    RTProperties rtp = this.getProperties();
    for (Iterator<?> i = rtp.keyIterator(); i.hasNext(); ) {
      Object key = i.next();
      if (!this._skipPropKey(key)) {
        if (!wrotePropHeader) {
          // out.write("\n");
          out.write("// Defined properties\n");
          wrotePropHeader = true;
        }
        String val[] = StringTools.parseStringArray(rtp.getProperty(key, "").toString(), '\n');
        String propVar = "PROP_" + key.toString().replace('.', '_').replace('-', '_');
        if (val.length == 1) {
          if (StringTools.isDouble(val[0], true)
              || StringTools.isLong(val[0], true)
              || StringTools.isBoolean(val[0], true)) {
            JavaScriptTools.writeJSVar(out, propVar, val[0], false);
          } else {
            JavaScriptTools.writeJSVar(out, propVar, val[0]);
          }
        } else if (val.length > 1) {
          JavaScriptTools.writeJSVar(out, propVar, StringTools.join(val, "\\n"));
        }
      }
    }

    /* speed units */
    Account.SpeedUnits speedUnits = reqState.getSpeedUnits();
    boolean speedIsKph = speedUnits.equals(Account.SpeedUnits.KPH);
    double altUnitsMult = speedIsKph ? 1.0 : GeoPoint.FEET_PER_METER;
    String altUnitsName =
        speedIsKph
            ? i18n.getString("JSMap.altitude.meters", "Meters")
            : i18n.getString("JSMap.altitude.feet", "Feet");

    /* constants (these do not change during the user session) */
    out.write("// Element IDs\n");
    JavaScriptTools.writeJSVar(out, "MAP_ID", this.getMapID());
    JavaScriptTools.writeJSVar(out, "ID_DETAIL_TABLE", ID_DETAIL_TABLE);
    JavaScriptTools.writeJSVar(out, "ID_DETAIL_CONTROL", ID_DETAIL_CONTROL);
    JavaScriptTools.writeJSVar(out, "ID_LAT_LON_DISPLAY", ID_LAT_LON_DISPLAY);
    JavaScriptTools.writeJSVar(out, "ID_DISTANCE_DISPLAY", ID_DISTANCE_DISPLAY);
    JavaScriptTools.writeJSVar(out, "ID_LATEST_EVENT_DATE", ID_LATEST_EVENT_DATE);
    JavaScriptTools.writeJSVar(out, "ID_LATEST_EVENT_TIME", ID_LATEST_EVENT_TIME);
    JavaScriptTools.writeJSVar(out, "ID_LATEST_EVENT_TMZ", ID_LATEST_EVENT_TMZ);
    JavaScriptTools.writeJSVar(out, "ID_LATEST_BATTERY", ID_LATEST_BATTERY);
    JavaScriptTools.writeJSVar(out, "ID_MESSAGE_TEXT", ID_MESSAGE_TEXT);
    out.write("// Geozone IDs\n");
    JavaScriptTools.writeJSVar(out, "ID_ZONE_LATITUDE_", ID_ZONE_LATITUDE_);
    JavaScriptTools.writeJSVar(out, "ID_ZONE_LONGITUDE_", ID_ZONE_LONGITUDE_);
    JavaScriptTools.writeJSVar(out, "ID_ZONE_RADIUS_M", ID_ZONE_RADIUS_M);
    out.write("// Session constants\n");
    JavaScriptTools.writeJSVar(out, "PUSHPINS_SHOW", rtp.getBoolean(PROP_map_pushpins, true));
    JavaScriptTools.writeJSVar(out, "MAX_PUSH_PINS", maxPushpins);
    JavaScriptTools.writeJSVar(out, "MAX_CREATION_AGE_SEC", rtp.getInt(PROP_map_maxCreationAge, 0));
    JavaScriptTools.writeJSVar(out, "MAP_WIDTH", this.getDimension().getWidth());
    JavaScriptTools.writeJSVar(out, "MAP_HEIGHT", this.getDimension().getHeight());
    JavaScriptTools.writeJSVar(out, "IS_FLEET", isFleet);
    JavaScriptTools.writeJSVar(
        out, "SHOW_SAT_COUNT", rtp.getBoolean(PROP_detail_showSatCount, false));
    JavaScriptTools.writeJSVar(out, "SHOW_SPEED", rtp.getBoolean(PROP_info_showSpeed, true));
    JavaScriptTools.writeJSVar(
        out, "COMBINE_SPEED_HEAD", rtp.getBoolean(PROP_combineSpeedHeading, true));
    JavaScriptTools.writeJSVar(out, "SHOW_ALTITUDE", rtp.getBoolean(PROP_info_showAltitude, false));
    JavaScriptTools.writeJSVar(out, "SHOW_ADDR", reqState.getShowAddress());
    JavaScriptTools.writeJSVar(
        out, "INCL_BLANK_ADDR", rtp.getBoolean(PROP_info_inclBlankAddress, true));
    JavaScriptTools.writeJSVar(
        out, "SHOW_OPT_FIELDS", rtp.getBoolean(PROP_info_showOptionalFields, true));
    JavaScriptTools.writeJSVar(
        out, "INCL_BLANK_OPT_FIELDS", rtp.getBoolean(PROP_info_inclBlankOptFields, true));
    JavaScriptTools.writeJSVar(
        out, "LATLON_FORMAT", Account.getLatLonFormat(account).getIntValue());
    JavaScriptTools.writeJSVar(
        out, "DISTANCE_KM_MULT", reqState.getDistanceUnits().getMultiplier());
    JavaScriptTools.writeJSVar(out, "SPEED_KPH_MULT", speedUnits.getMultiplier());
    JavaScriptTools.writeJSVar(out, "SPEED_UNITS", speedUnits.toString(locale));
    JavaScriptTools.writeJSVar(out, "ALTITUDE_METERS_MULT", altUnitsMult);
    JavaScriptTools.writeJSVar(out, "ALTITUDE_UNITS", altUnitsName);
    JavaScriptTools.writeJSVar(out, "TIME_ZONE", reqState.getTimeZoneString(null)); // long
    JavaScriptTools.writeJSVar(
        out,
        "DEFAULT_CENTER",
        "{ lat:" + dftCenter.getLatitude() + ", lon:" + dftCenter.getLongitude() + " }",
        false);
    JavaScriptTools.writeJSVar(out, "DEFAULT_ZOOM", this.getDefaultZoom(JSMap.DEFAULT_ZOOM, false));
    JavaScriptTools.writeJSVar(out, "PUSHPIN_ZOOM", this.getDefaultZoom(JSMap.PUSHPIN_ZOOM, true));
    JavaScriptTools.writeJSVar(out, "MAP_AUTHORIZATION", this.getAuthorization());
    JavaScriptTools.writeJSVar(
        out, "SCROLL_WHEEL_ZOOM", rtp.getBoolean(PROP_scrollWheelZoom, false));
    JavaScriptTools.writeJSVar(out, "DEFAULT_VIEW", rtp.getString(PROP_map_view, "").toLowerCase());
    JavaScriptTools.writeJSVar(out, "ROUTE_LINE_SHOW", rtp.getBoolean(PROP_map_routeLine, true));
    JavaScriptTools.writeJSVar(
        out, "ROUTE_LINE_COLOR", rtp.getString(PROP_map_routeLine_color, "#FF2222"));
    JavaScriptTools.writeJSVar(
        out, "ROUTE_LINE_ARROWS", rtp.getBoolean(PROP_map_routeLine_arrows, false));
    JavaScriptTools.writeJSVar(
        out,
        "ROUTE_SNAP_TO_ROAD",
        rtp.getBoolean(PROP_map_routeLine_snapToRoad, false)); // Google V2 only
    JavaScriptTools.writeJSVar(out, "REPLAY_INTERVAL", this.getReplayInterval());
    JavaScriptTools.writeJSVar(out, "REPLAY_SINGLE", this.getReplaySinglePushpin());

    /* address title */
    String adrTitles[] = reqState.getAddressTitles();
    String adrTitle = ListTools.itemAt(adrTitles, 0, null);

    /* device title */
    String devTitles[] = reqState.getDeviceTitles();
    String devTitle = ListTools.itemAt(devTitles, 0, null);

    /* labels */
    out.write("// Localized Text/Labels\n");
    JavaScriptTools.writeJSVar(
        out,
        "HEADING",
        "new Array("
            + "\""
            + GeoPoint.CompassHeading.N.toString(locale)
            + "\","
            + "\""
            + GeoPoint.CompassHeading.NE.toString(locale)
            + "\","
            + "\""
            + GeoPoint.CompassHeading.E.toString(locale)
            + "\","
            + "\""
            + GeoPoint.CompassHeading.SE.toString(locale)
            + "\","
            + "\""
            + GeoPoint.CompassHeading.S.toString(locale)
            + "\","
            + "\""
            + GeoPoint.CompassHeading.SW.toString(locale)
            + "\","
            + "\""
            + GeoPoint.CompassHeading.W.toString(locale)
            + "\","
            + "\""
            + GeoPoint.CompassHeading.NW.toString(locale)
            + "\")",
        false);
    JavaScriptTools.writeJSVar(out, "TEXT_INFO_DATE", i18n.getString("JSMap.info.date", "Date"));
    JavaScriptTools.writeJSVar(out, "TEXT_INFO_GPS", i18n.getString("JSMap.info.gps", "GPS"));
    JavaScriptTools.writeJSVar(out, "TEXT_INFO_SATS", i18n.getString("JSMap.info.sats", "#Sats"));
    JavaScriptTools.writeJSVar(out, "TEXT_INFO_SPEED", i18n.getString("JSMap.info.speed", "Speed"));
    JavaScriptTools.writeJSVar(out, "TEXT_INFO_HEADING", GeoPoint.GetHeadingTitle(locale));
    JavaScriptTools.writeJSVar(
        out, "TEXT_INFO_ALTITUDE", i18n.getString("JSMap.info.altitude", "Altitude"));
    JavaScriptTools.writeJSVar(
        out, "TEXT_INFO_STOP_TIME", i18n.getString("JSMap.info.stopTime", "Stop Time"));
    JavaScriptTools.writeJSVar(
        out,
        "TEXT_INFO_ADDR",
        !StringTools.isBlank(adrTitle)
            ? adrTitle
            : i18n.getString("JSMap.info.address", "Address"));
    JavaScriptTools.writeJSVar(
        out,
        "TEXT_DEVICE",
        !StringTools.isBlank(devTitle) ? devTitle : i18n.getString("JSMap.device", "Device"));
    JavaScriptTools.writeJSVar(out, "TEXT_DATE", i18n.getString("JSMap.dateTime", "Date/Time"));
    JavaScriptTools.writeJSVar(out, "TEXT_CODE", i18n.getString("JSMap.code", "Status"));
    JavaScriptTools.writeJSVar(out, "TEXT_LATLON", i18n.getString("JSMap.latLon", "Lat/Lon"));
    JavaScriptTools.writeJSVar(out, "TEXT_SATCOUNT", i18n.getString("JSMap.satCount", "#Sats"));
    JavaScriptTools.writeJSVar(
        out,
        "TEXT_ADDR",
        !StringTools.isBlank(adrTitle) ? adrTitle : i18n.getString("JSMap.address", "Address"));
    JavaScriptTools.writeJSVar(out, "TEXT_SPEED", reqState.getSpeedUnits().toString(locale));
    JavaScriptTools.writeJSVar(out, "TEXT_HEADING", i18n.getString("JSMap.heading", "Heading"));
    JavaScriptTools.writeJSVar(out, "TEXT_DISTANCE", reqState.getDistanceUnits().toString(locale));
    JavaScriptTools.writeJSVar(
        out,
        "TEXT_TIMEOUT",
        i18n.getString("JSMap.sessionTimeout", "Your session has timed-out.\nPlease login ..."));
    JavaScriptTools.writeJSVar(
        out,
        "TEXT_PING_OK",
        i18n.getString(
            "JSMap.pingDevice.ok",
            "A command request has been sent.\nThe {0} should respond shortly ...",
            devTitles));
    JavaScriptTools.writeJSVar(
        out,
        "TEXT_PING_ERROR",
        i18n.getString(
            "JSMap.pingDevice.err",
            "The command request failed.\nThe {0} may not support this feature ...",
            devTitles));
    JavaScriptTools.writeJSVar(
        out,
        "TEXT_MAXPUSHPINS_ALERT",
        i18n.getString(
            "JSMap.maxPushpins.err",
            "The maximum number of allowed pushpins has been exceeded.\n"
                + " [max={0}] Not all pushpins may be displayed on this map.\n"
                + "Adjust the 'From' time to see remaining pushpins",
            String.valueOf(maxPushpins)));
    JavaScriptTools.writeJSVar(
        out,
        "TEXT_MAXPUSHPINS_MSG",
        i18n.getString(
            "JSMap.maxPushpins.msg",
            "Only partial data displayed.  The maximum allowed pushpins has been reached.<BR>"
                + "Adjust the Date/Time range accordingly to view the remaining pushpins."));
    JavaScriptTools.writeJSVar(
        out, "TEXT_UNAVAILABLE", i18n.getString("JSMap.unavailable", "unavailable"));
    JavaScriptTools.writeJSVar(
        out,
        "TEXT_showLocationDetails",
        i18n.getString("JSMap.showLocationDetails", "Show Location Details"));
    JavaScriptTools.writeJSVar(
        out,
        "TEXT_hideLocationDetails",
        i18n.getString("JSMap.hideLocationDetails", "Hide Location Details"));

    /* map "Loading ..." */
    JavaScriptTools.writeJSVar(
        out,
        "TEXT_LOADING_MAP_POINTS",
        (rtp.getBoolean(PROP_MAP_LOADING, false)
            ? i18n.getString("JSMap.loadingMapPoints", "Loading Map Points ...")
            : null));
    JavaScriptTools.writeJSVar(
        out, "MAP_LOADING_IMAGE_URI", rtp.getString(PROP_MAP_LOADING_IMAGE, null));

    /* icons/shadows */
    JSMap.writePushpinArray(out, reqState);

    /* constants (these do not change during the user session) */
    out.write("// Geozone support constants\n");
    JavaScriptTools.writeJSVar(out, "jsvGeozoneMode", false);
    JavaScriptTools.writeJSVar(out, "MAX_ZONE_RADIUS_M", Geozone.MAX_RADIUS_METERS);
    JavaScriptTools.writeJSVar(out, "MIN_ZONE_RADIUS_M", Geozone.MIN_RADIUS_METERS);
    JavaScriptTools.writeJSVar(
        out, "DETAIL_REPORT", this.isFeatureSupported(FEATURE_DETAIL_REPORT));
    JavaScriptTools.writeJSVar(
        out, "DETAIL_INFO_BOX", this.isFeatureSupported(FEATURE_DETAIL_INFO_BOX));
    JavaScriptTools.writeJSVar(out, "TEXT_METERS", GeoPoint.DistanceUnits.METERS.toString(locale));

    /* variables */
    out.write("// TrackMap Vars\n");
    JavaScriptTools.writeJSVar(out, "jsvPoiPins", null);
    JavaScriptTools.writeJSVar(out, "jsvDataSets", null);
    JavaScriptTools.writeJSVar(out, "jsvDetailPoints", null);
    JavaScriptTools.writeJSVar(out, "jsvDetailVisible", false);
    JavaScriptTools.writeJSVar(
        out,
        "jsvDetailAscending",
        privLabel.getBooleanProperty(PrivateLabel.PROP_TrackMap_detailAscending, true));
    JavaScriptTools.writeJSVar(
        out,
        "jsvDetailCenterPushpin",
        privLabel.getBooleanProperty(PrivateLabel.PROP_TrackMap_detailCenterPushpin, false));

    /* last update time */
    TimeZone tmz = reqState.getTimeZone();
    String dateFmt =
        (account != null) ? account.getDateFormat() : BasicPrivateLabel.getDefaultDateFormat();
    String timeFmt =
        (account != null) ? account.getTimeFormat() : BasicPrivateLabel.getDefaultTimeFormat();
    DateTime today = new DateTime(tmz);
    JavaScriptTools.writeJSVar(out, "jsvTodayEpoch", today.getTimeSec());
    JavaScriptTools.writeJSVar(
        out,
        "jsvTodayYMD",
        "{ YYYY:"
            + today.getYear(tmz)
            + ", MM:"
            + today.getMonth1(tmz)
            + ", DD:"
            + today.getDayOfMonth(tmz)
            + " }",
        false);
    JavaScriptTools.writeJSVar(out, "jsvTodayDateFmt", today.format(dateFmt, tmz));
    JavaScriptTools.writeJSVar(out, "jsvTodayTimeFmt", today.format(timeFmt, tmz));
    JavaScriptTools.writeJSVar(out, "jsvTodayTmzFmt", today.format("z", tmz));

    /* last event time */
    out.write("// Last event time\n");
    DateTime lastEventTime = reqState.getLastEventTime();
    if (lastEventTime != null) {
      JavaScriptTools.writeJSVar(out, "jsvLastEventEpoch", lastEventTime.getTimeSec());
      JavaScriptTools.writeJSVar(
          out,
          "jsvLastEventYMD",
          "{ YYYY:"
              + lastEventTime.getYear(tmz)
              + ", MM:"
              + lastEventTime.getMonth1(tmz)
              + ", DD:"
              + lastEventTime.getDayOfMonth(tmz)
              + " }",
          false);
      JavaScriptTools.writeJSVar(out, "jsvLastEventDateFmt", lastEventTime.format(dateFmt, tmz));
      JavaScriptTools.writeJSVar(out, "jsvLastEventTimeFmt", lastEventTime.format(timeFmt, tmz));
      JavaScriptTools.writeJSVar(out, "jsvLastEventTmzFmt", lastEventTime.format("z", tmz));
      JavaScriptTools.writeJSVar(out, "jsvLastBatteryLevel", 0L);
      JavaScriptTools.writeJSVar(out, "jsvLastSignalStrength", 0L);
    } else {
      JavaScriptTools.writeJSVar(out, "jsvLastEventEpoch", 0L);
      JavaScriptTools.writeJSVar(out, "jsvLastEventYMD", null);
      JavaScriptTools.writeJSVar(out, "jsvLastEventDateFmt", null);
      JavaScriptTools.writeJSVar(out, "jsvLastEventTimeFmt", null);
      JavaScriptTools.writeJSVar(out, "jsvLastEventTmzFmt", null);
      JavaScriptTools.writeJSVar(out, "jsvLastBatteryLevel", 0.0);
      JavaScriptTools.writeJSVar(out, "jsvLastSignalStrength", 0.0);
    }

    /* map pointers */
    out.write("// Map vars\n");
    JavaScriptTools.writeJSVar(out, "jsmapElem", null);
    JavaScriptTools.writeJSVar(out, "jsmap", null);
  }