← back to Commercialrealestate

public/crcp-cities.js

59 lines

/* crcp-cities.js — single source of truth for the *.crcp.agentabrams.com city fleet.
 * Used by recities.html (the launcher hub) AND crcp-scope.js (per-city grid scoping).
 * To add/remove a city market: add its subdomain vhost + DNS, then add the slug here. */
(function () {
  // Multi-word / special-cased labels; single-word slugs fall back to Title Case.
  var LABELS = {
    belair: "Bel-Air", beverlyhills: "Beverly Hills", canyondam: "Canyon Dam", chinesecamp: "Chinese Camp",
    crescentmills: "Crescent Mills", echolake: "Echo Lake", forestranch: "Forest Ranch", grassvalley: "Grass Valley",
    grizzlyflats: "Grizzly Flats", klamathriver: "Klamath River", lakeelsinore: "Lake Elsinore", northfork: "North Fork",
    pacificpalisades: "Pacific Palisades", paynescreek: "Paynes Creek", sanjuancapistrano: "San Juan Capistrano",
    santamonica: "Santa Monica", shermanoaks: "Sherman Oaks", sierramadre: "Sierra Madre", southlaketahoe: "South Lake Tahoe",
    studiocity: "Studio City", twinbridges: "Twin Bridges", woffordheights: "Wofford Heights"
  };
  var SLUGS = ["altadena", "belair", "beverlyhills", "caliente", "camarillo", "canyondam", "chester", "chico",
    "chinesecamp", "clearlake", "cohasset", "colfax", "crescentmills", "doyle", "echolake", "encino", "foresthill",
    "forestranch", "georgetown", "grassvalley", "greenville", "grizzlyflats", "havilah", "hemet", "janesville",
    "klamathriver", "lakeelsinore", "lakehead", "malibu", "mariposa", "mineral", "northfork", "pacificpalisades",
    "pasadena", "paynescreek", "redding", "sanjuancapistrano", "santamonica", "shermanoaks", "sierramadre", "somerset",
    "somis", "southlaketahoe", "studiocity", "topanga", "twinbridges", "ukiah", "weed", "woffordheights", "wrightwood"];

  // Hosts that are NOT a city (serve the app unscoped, or a hub). label-0 of these -> no scope.
  var RESERVED = { crcp: 1, recities: 1, firecities: 1, www: 1, localhost: 1, "127": 1 };

  // Fire-affected burn-zone cities (2025 LA firestorm) — the firecities.crcp hub scopes to these.
  // fire = the incident label shown in the hub. Only slugs that are also real fleet cities.
  var FIRE = {
    altadena: 'Eaton Fire', pasadena: 'Eaton Fire', sierramadre: 'Eaton Fire',
    pacificpalisades: 'Palisades Fire', malibu: 'Palisades Fire', topanga: 'Palisades Fire',
    santamonica: 'Palisades Fire'
  };

  function title(s) { return s.replace(/(^|\s)\S/g, function (c) { return c.toUpperCase(); }); }
  function norm(s) { return String(s == null ? "" : s).toLowerCase().replace(/[^a-z]/g, ""); }

  var CITIES = SLUGS.map(function (slug) {
    var label = LABELS[slug] || title(slug);
    return { slug: slug, label: label, host: slug + ".crcp.agentabrams.com", norm: norm(label), fire: FIRE[slug] || null };
  });
  var BY_SLUG = {}; CITIES.forEach(function (c) { BY_SLUG[c.slug] = c; });
  var FIRE_CITIES = CITIES.filter(function (c) { return c.fire; });

  // Resolve the current host to a city object, or null when unscoped (base crcp / recities / ip / localhost).
  function cityFromHost(host) {
    var label0 = String(host || "").toLowerCase().split(".")[0];
    if (!label0 || RESERVED[label0] || /^\d+$/.test(label0)) return null;
    return BY_SLUG[label0] || null;
  }
  // Does a listing's city string belong to this city object? Strip a trailing state suffix
  // ("Pacific Palisades, CA" -> "Pacific Palisades") BEFORE norm — norm() removes non-letters,
  // so without this a standard "City, ST" data source would norm to "pacificpalisadesca" and
  // silently miss the match (under-scoping). split guards null via the String() coercion.
  function sameCity(cityStr, cityObj) { return !!cityObj && norm(String(cityStr == null ? "" : cityStr).split(",")[0]) === cityObj.norm; }

  window.CRCP_CITIES = {
    LABELS: LABELS, SLUGS: SLUGS, CITIES: CITIES, BY_SLUG: BY_SLUG, FIRE: FIRE, FIRE_CITIES: FIRE_CITIES,
    cityFromHost: cityFromHost, sameCity: sameCity, norm: norm, title: title
  };
})();