← 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
};
})();