@bongos/core 1.21.112 → 1.21.114
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.bongos-core.json +616 -186
- package/.claude/skills/ask-for-help/SKILL.md +1 -1
- package/.claude/skills/bongos-feedback/SKILL.md +1 -1
- package/.claude/skills/builder-sequence/SKILL.md +1 -1
- package/.claude/skills/builder-ship/SKILL.md +1 -0
- package/GOVERNMENT.md +128 -11
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +28 -0
- package/clients/bongos-client/index.cjs +28 -0
- package/clients/bongos-client/index.d.ts +45 -2
- package/clients/bongos-client/index.mjs +28 -0
- package/docs/adr/0372-an-ask-is-stored-per-docket-matter-and-the-server-refuses-slop.md +33 -0
- package/docs/adr/0373-an-ai-session-files-a-blocker-with-an-ask.md +31 -0
- package/docs/adr/0374-a-building-lead-is-a-held-position-appointed-by-a-people-matter.md +30 -0
- package/docs/adr/0375-who-may-build-without-a-vote-comes-from-the-form-of-government.md +38 -0
- package/docs/adr/0376-every-ask-is-routed-to-a-real-seat-on-the-server.md +30 -0
- package/docs/adr/0377-governance-is-a-held-position-read-on-me.md +24 -0
- package/docs/adr/0378-a-leads-build-outs-are-rows-built-at-once-or-by-a-passed-vote.md +33 -0
- package/docs/adr/0379-the-treasury-is-pots-per-goal-a-reserve-and-borrowing-over-real-spend.md +32 -0
- package/docs/adr/0380-a-strictness-preset-is-a-bundle-of-laws-switched-by-one-laws-vote.md +26 -0
- package/docs/adr/README.md +10 -1
- package/docs/api/openapi.json +989 -87
- package/docs/api-reference.md +18 -4
- package/docs/copy-inventory.md +343 -262
- package/docs/copy-registry.json +1223 -426
- package/docs/file-map.md +2 -1
- package/docs/module-api-changelog.md +4 -0
- package/docs/packs/engineer.md +2 -1
- package/docs/page-inventory.json +91 -6
- package/docs/page-readings.json +5017 -4458
- package/modules/builder-settings/law-locks.js +202 -0
- package/modules/builder-settings/routes/settings.js +66 -6
- package/modules/builder-settings/settings.js +7 -0
- package/modules/discord/ship-broadcast.js +22 -13
- package/modules/government/asks-db.js +54 -0
- package/modules/government/asks.js +269 -0
- package/modules/government/board-idea-reads.js +68 -0
- package/modules/government/board.js +21 -57
- package/modules/government/build-rights.js +148 -0
- package/modules/government/buildouts.js +160 -0
- package/modules/government/counts.js +81 -0
- package/modules/government/db.js +242 -0
- package/modules/government/decision-board.js +234 -8
- package/modules/government/decision-rules.js +2 -0
- package/modules/government/docket.js +114 -6
- package/modules/government/law-board.js +131 -0
- package/modules/government/laws.js +241 -0
- package/modules/government/leads.js +170 -0
- package/modules/government/migrations/government_027_laws.sql +143 -0
- package/modules/government/migrations/government_028_asks.sql +57 -0
- package/modules/government/migrations/government_029_leads.sql +135 -0
- package/modules/government/migrations/government_030_buildouts.sql +111 -0
- package/modules/government/migrations/government_031_treasury.sql +107 -0
- package/modules/government/module.json +2 -1
- package/modules/government/pollers/treasury-borrow.js +62 -0
- package/modules/government/positions.js +107 -0
- package/modules/government/presets.js +149 -0
- package/modules/government/routes/government.js +425 -2
- package/modules/government/routing.js +154 -0
- package/modules/government/treasury-db.js +166 -0
- package/modules/government/treasury.js +241 -0
- package/modules/hall-ui/public/atlas.html +2 -2
- package/modules/hall-ui/public/blockers.html +2 -2
- package/modules/hall-ui/public/board-room.html +2 -2
- package/modules/hall-ui/public/board-room.js +2 -0
- package/modules/hall-ui/public/city-draw.js +604 -11
- package/modules/hall-ui/public/city.css +157 -1
- package/modules/hall-ui/public/city.html +37 -7
- package/modules/hall-ui/public/city.js +441 -21
- package/modules/hall-ui/public/city.states.json +110 -5
- package/modules/hall-ui/public/collab.html +2 -2
- package/modules/hall-ui/public/commands.html +2 -2
- package/modules/hall-ui/public/copy-desk.html +2 -2
- package/modules/hall-ui/public/craft-mode.js +112 -29
- package/modules/hall-ui/public/deploy.html +2 -2
- package/modules/hall-ui/public/diagram-viewer.css +12 -0
- package/modules/hall-ui/public/diagram-viewer.js +4 -0
- package/modules/hall-ui/public/diagrams.html +2 -2
- package/modules/hall-ui/public/drachmae.html +2 -2
- package/modules/hall-ui/public/fleet.html +2 -2
- package/modules/hall-ui/public/gate.html +2 -2
- package/modules/hall-ui/public/goal-map.html +2 -2
- package/modules/hall-ui/public/goals.html +2 -2
- package/modules/hall-ui/public/gov-feeds.js +141 -0
- package/modules/hall-ui/public/gov-living.js +1002 -0
- package/modules/hall-ui/public/gov-positions.js +97 -0
- package/modules/hall-ui/public/government.html +2 -2
- package/modules/hall-ui/public/hall-render.js +3 -0
- package/modules/hall-ui/public/idea.html +2 -2
- package/modules/hall-ui/public/ideas.html +2 -2
- package/modules/hall-ui/public/index.html +3 -3
- package/modules/hall-ui/public/modules.html +2 -2
- package/modules/hall-ui/public/my-agents.css +334 -0
- package/modules/hall-ui/public/my-agents.html +72 -0
- package/modules/hall-ui/public/my-agents.js +603 -0
- package/modules/hall-ui/public/my-agents.states.json +37 -0
- package/modules/hall-ui/public/primer.html +2 -2
- package/modules/hall-ui/public/profile-connections.js +40 -28
- package/modules/hall-ui/public/profile.html +2 -2
- package/modules/hall-ui/public/project-settings.html +2 -2
- package/modules/hall-ui/public/ranks.html +2 -2
- package/modules/hall-ui/public/roadmap.html +2 -2
- package/modules/hall-ui/public/room.css +8 -0
- package/modules/hall-ui/public/roster.html +2 -2
- package/modules/hall-ui/public/sessions.html +2 -2
- package/modules/hall-ui/public/settings.html +2 -2
- package/modules/hall-ui/public/shell.js +116 -16
- package/modules/hall-ui/public/street-card.js +18 -1
- package/modules/hall-ui/public/street-deck.css +299 -0
- package/modules/hall-ui/public/street-deck.js +1145 -0
- package/modules/hall-ui/public/street-depth.js +712 -0
- package/modules/hall-ui/public/street-desk.js +5 -2
- package/modules/hall-ui/public/street-draw.js +109 -16
- package/modules/hall-ui/public/street-inside.js +137 -22
- package/modules/hall-ui/public/street-office.js +705 -0
- package/modules/hall-ui/public/street.css +161 -11
- package/modules/hall-ui/public/street.html +41 -11
- package/modules/hall-ui/public/street.js +78 -66
- package/modules/hall-ui/public/street.states.json +129 -21
- package/modules/hall-ui/public/studio.html +2 -2
- package/modules/hall-ui/public/style.css +5 -1
- package/modules/hall-ui/public/task.html +2 -2
- package/modules/hall-ui/public/thinking.html +2 -2
- package/modules/hall-ui/public/tweak-editor.html +2 -2
- package/modules/hall-ui/public/watch.html +2 -2
- package/modules/hall-ui/public/watch.js +14 -1
- package/modules/hall-ui/public/work.html +2 -2
- package/modules/hall-ui/records/living-city.md +57 -0
- package/modules/hall-ui/records/my-agents.md +21 -0
- package/modules/hall-ui/records/oversight-family.md +1 -0
- package/modules/hall-ui/records/profile-connections.md +10 -0
- package/modules/hall-ui/records/reading-rooms.md +2 -0
- package/modules/ideas/routes/blockers.js +92 -3
- package/modules/lifecycle/module.json +2 -1
- package/modules/lifecycle/moving.js +17 -1
- package/modules/lifecycle/routes/moving.js +6 -0
- package/modules/lifecycle/routes/project-settings.js +23 -0
- package/modules/onboarding/approval-broadcast.js +8 -0
- package/modules/onboarding/routes/access-requests.js +16 -6
- package/modules/platform-identity/connections.js +13 -2
- package/modules/platform-identity/routes/connections.js +7 -1
- package/modules/public-landing/public/account-private.states.json +2 -2
- package/modules/public-landing/public/account.html +65 -5
- package/modules/public-landing/public/account.states.json +3 -3
- package/modules/public-landing/public/assets/world/r2-bongos-chrome-b.jpg.json +1 -1
- package/modules/public-landing/public/assets/world/r2-bongos-chrome-b.webp +0 -0
- package/modules/public-landing/public/assets/world/r2-bongos-chrome-b.webp.json +13 -0
- package/modules/public-landing/public/assets/world/r2-chrome-blob-one.jpg.json +1 -1
- package/modules/public-landing/public/assets/world/r2-chrome-blob-one.webp +0 -0
- package/modules/public-landing/public/assets/world/r2-chrome-blob-one.webp.json +13 -0
- package/modules/public-landing/public/assets/world/r2-chrome-blobs-set.jpg.json +1 -1
- package/modules/public-landing/public/assets/world/r2-chrome-blobs-set.webp +0 -0
- package/modules/public-landing/public/assets/world/r2-chrome-blobs-set.webp.json +13 -0
- package/modules/public-landing/public/assets/world.css +4 -4
- package/modules/public-landing/public/connections-empty.states.json +14 -0
- package/modules/public-landing/public/connections-inactive.states.json +14 -0
- package/modules/public-landing/public/connections.html +450 -0
- package/modules/public-landing/public/connections.probes.json +414 -0
- package/modules/public-landing/public/connections.states.json +17 -0
- package/modules/public-landing/public/contact.html +2 -2
- package/modules/public-landing/public/guild.html +2 -2
- package/modules/public-landing/public/index.html +2 -2
- package/modules/public-landing/public/invitations.html +2 -2
- package/modules/public-landing/public/privacy.html +2 -2
- package/modules/public-landing/public/projects.html +2 -2
- package/modules/public-landing/public/projects.probes.json +6 -2
- package/modules/public-landing/public/terms.html +2 -2
- package/modules/ui-design/kit/fixtures/connections-empty.json +12 -0
- package/modules/ui-design/kit/fixtures/connections-inactive.json +7 -0
- package/modules/ui-design/kit/fixtures/connections.json +22 -0
- package/modules/ui-design/kit/serve.js +95 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +128 -0
- package/scripts/discord/README.md +8 -5
- package/scripts/discord/post.js +12 -1
- package/scripts/gds/artifact-audit.js +20 -3
- package/scripts/gds/ask.js +164 -0
- package/scripts/gds/autobongos-run.js +20 -5
- package/scripts/gds/autobongos-tweak-lane.js +262 -0
- package/scripts/gds/autobongos-verify.js +21 -2
- package/scripts/gds/claim.js +2 -0
- package/scripts/gds/government-walk.js +638 -4
- package/scripts/gds/pulse.js +9 -6
- package/scripts/gds/ship-escalation.js +22 -3
- package/scripts/gds/ship-finish.js +5 -4
- package/scripts/gds/ship-merge-gate.js +18 -6
- package/scripts/gds/tweak-halt.js +1 -0
- package/scripts/hall-preview/README.md +41 -1
- package/scripts/hall-preview/pref-writes.js +140 -0
- package/scripts/hall-preview/refresh-fixtures.js +101 -7
- package/scripts/hall-preview/scrub-identity.js +366 -7
- package/scripts/hall-preview/server.js +27 -1
- package/scripts/render-diagrams.sh +4 -2
- package/src/bongos/auth-idp.js +6 -1
- package/src/bongos/project-settings.js +41 -0
- package/src/bongos/route-rank-check.js +27 -0
- package/src/bongos/routes/me.js +68 -7
- package/src/bongos/serve-internal.js +5 -1
- package/src/module-api.js +7 -1
- package/src/platform-server.js +10 -0
- package/tests/application_lifecycle.mjs +47 -5
- package/tests/autobongos_tweak_lane.mjs +208 -0
- package/tests/conductor_main_e2e.mjs +21 -0
- package/tests/connections_api.mjs +18 -0
- package/tests/craft_mode_nav.mjs +5 -3
- package/tests/craft_mode_tallies.mjs +52 -16
- package/tests/discord_approvals.mjs +11 -0
- package/tests/discord_channels.mjs +20 -0
- package/tests/fixtures/gov-seated.mjs +64 -0
- package/tests/fixtures/government-ask-cards.json +1284 -0
- package/tests/government_act_alone_buildings.mjs +1 -0
- package/tests/government_agent_asks.mjs +322 -0
- package/tests/government_asks.mjs +408 -0
- package/tests/government_build_rights.mjs +253 -0
- package/tests/government_buildouts.mjs +382 -0
- package/tests/government_counts.mjs +143 -0
- package/tests/government_decision_rules.mjs +5 -1
- package/tests/government_laws.mjs +488 -0
- package/tests/government_laws_session.mjs +321 -0
- package/tests/government_leads.mjs +375 -0
- package/tests/government_positions.mjs +127 -0
- package/tests/government_presets.mjs +215 -0
- package/tests/government_routes.mjs +8 -1
- package/tests/government_routing.mjs +118 -0
- package/tests/government_seed.mjs +5 -0
- package/tests/government_treasury.mjs +394 -0
- package/tests/governor_docket.mjs +26 -2
- package/tests/hall_buildouts.mjs +208 -0
- package/tests/hall_city.mjs +14 -6
- package/tests/hall_city_living.mjs +617 -0
- package/tests/hall_gov_counts.mjs +336 -0
- package/tests/hall_gov_living.mjs +636 -0
- package/tests/hall_gov_real_site.mjs +655 -0
- package/tests/hall_matter_card.mjs +6 -1
- package/tests/hall_my_agents.mjs +481 -0
- package/tests/hall_nav.mjs +107 -4
- package/tests/hall_page_gate_map.mjs +4 -0
- package/tests/hall_preview_governor_fixtures.mjs +30 -3
- package/tests/hall_preview_scrub.mjs +59 -1
- package/tests/hall_preview_security.mjs +169 -0
- package/tests/hall_reading_world.mjs +44 -0
- package/tests/hall_roster_world.mjs +6 -2
- package/tests/hall_street.mjs +18 -2
- package/tests/hall_street_amend.mjs +1 -1
- package/tests/hall_street_deck.mjs +609 -0
- package/tests/hall_street_depth.mjs +559 -0
- package/tests/hall_street_desk.mjs +1 -1
- package/tests/hall_street_inside.mjs +29 -5
- package/tests/hall_tweak_editor.mjs +4 -1
- package/tests/hub_account_page.mjs +94 -3
- package/tests/hub_connections_page.mjs +507 -0
- package/tests/hub_invite_badge.mjs +1 -1
- package/tests/idp_device_exchange.mjs +18 -0
- package/tests/landing_legal_pages.mjs +8 -5
- package/tests/landing_page.mjs +23 -4
- package/tests/nav_permission_atoms.mjs +4 -1
- package/tests/profile_connections_ui.mjs +88 -25
- package/tests/projects_hub.mjs +15 -0
- package/tests/render_check.mjs +2 -2
- package/tests/ship_broadcast_routing.mjs +57 -16
- package/tests/ship_broadcast_server.mjs +40 -7
- package/tests/ship_merge_gate.mjs +19 -0
- package/tests/ui_design_kit.mjs +2 -2
- package/tests/watch_applications_queue.mjs +23 -0
|
@@ -1,19 +1,28 @@
|
|
|
1
1
|
// modules/hall-ui/public/city.js — THE GOVERNOR CITY's home, wired (task
|
|
2
|
-
// 1004524, goal 1000125, R02).
|
|
2
|
+
// 1004524, goal 1000125, R02; alive since task 1004685).
|
|
3
3
|
//
|
|
4
|
-
// city-draw.js computes the city as data; this file reads the
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// one per pin
|
|
8
|
-
//
|
|
9
|
-
//
|
|
4
|
+
// city-draw.js computes the city as data; this file reads the feeds, hands
|
|
5
|
+
// them to GovCity.cityModel(), and turns its lists into DOM: SVG paths for
|
|
6
|
+
// the ground and the buildings, one span per parcel and per sky pod (CSS
|
|
7
|
+
// offset-path), one per pin, light shaft and Lead plate, and one button per
|
|
8
|
+
// civic building that opens its card in place. It also owns the sun/moon
|
|
9
|
+
// switch and the stage's fit to the window. No innerHTML anywhere: every node
|
|
10
|
+
// is created, and every docket string lands as text or an attribute.
|
|
10
11
|
//
|
|
11
12
|
// THE FEEDS (all GETs, all gated server-side):
|
|
12
13
|
// /api/bongos/government/docket the pins and the one line (task 1004523)
|
|
13
14
|
// /api/bongos/goals the open goals: one district each
|
|
14
15
|
// /api/bongos/goals/rollups each goal's task counts: a building per task
|
|
15
|
-
//
|
|
16
|
-
//
|
|
16
|
+
// and, for the living city (task 1004685), the reads gov-living.js takes:
|
|
17
|
+
// government/constitution (the plaque's form of government), tasks/claimable
|
|
18
|
+
// (the active claims: one sky pod each), tasks?status=active (which goal a
|
|
19
|
+
// claim's task is in), public/cost-summary (the spend conduit and the
|
|
20
|
+
// plaque), builders/roster and /me (who leads, who the viewer is), blockers,
|
|
21
|
+
// autonomy/fence, moving, and the government's own reads (laws, Leads, the
|
|
22
|
+
// treasury, the presets, the counts). All of it is real: the prototype
|
|
23
|
+
// overlay is gone (task 1004717).
|
|
24
|
+
// A feed that fails leaves its part of the city out; it never draws sample
|
|
25
|
+
// data. Without gov-living.js the page draws the city it drew before.
|
|
17
26
|
//
|
|
18
27
|
// THE SWITCH. Light and dark use the hall's own preference ('hall-theme' in
|
|
19
28
|
// localStorage, else the OS setting) and the hall's own attribute
|
|
@@ -22,7 +31,12 @@
|
|
|
22
31
|
// the mode, so a toggle redraws the city from the last feeds (no refetch).
|
|
23
32
|
//
|
|
24
33
|
// MOTION is CSS only (city.css); nothing here animates. The scale write on
|
|
25
|
-
// resize is layout, not motion.
|
|
34
|
+
// resize is layout, not motion; the card changes a class.
|
|
35
|
+
//
|
|
36
|
+
// ANSWERING. A building's card takes its first ask in place (task 1004702, the
|
|
37
|
+
// owner's pick) through the deck's own sender (street-deck.js GovDeck.sendAnswer:
|
|
38
|
+
// the desk row's real route, never a new one); its door leads to the decision
|
|
39
|
+
// deck on the street (/street#deck=<building>) for the rest.
|
|
26
40
|
|
|
27
41
|
(function () {
|
|
28
42
|
'use strict';
|
|
@@ -36,10 +50,33 @@
|
|
|
36
50
|
goals: '/api/bongos/goals',
|
|
37
51
|
rollups: '/api/bongos/goals/rollups',
|
|
38
52
|
};
|
|
53
|
+
// the living city's reads (task 1004685), fetched only when gov-living.js is on the page
|
|
54
|
+
var LIVING_FEEDS = {
|
|
55
|
+
// the counts every surface prints, answered once by the server (task 1004716)
|
|
56
|
+
counts: '/api/bongos/government/counts',
|
|
57
|
+
constitution: '/api/bongos/government/constitution',
|
|
58
|
+
// the project's laws in force (task 1004706)
|
|
59
|
+
laws: '/api/bongos/government/laws',
|
|
60
|
+
// who leads each building, and the owner seat an unseated one falls to (task 1004709)
|
|
61
|
+
leads: '/api/bongos/government/leads',
|
|
62
|
+
// the pots each goal draws from, the reserve, the spend cap (task 1004712)
|
|
63
|
+
treasury: '/api/bongos/government/treasury',
|
|
64
|
+
claims: '/api/bongos/tasks/claimable?limit=1',
|
|
65
|
+
tasks: '/api/bongos/tasks?status=active&limit=200',
|
|
66
|
+
cost: '/api/bongos/public/cost-summary',
|
|
67
|
+
roster: '/api/bongos/builders/roster',
|
|
68
|
+
me: '/api/bongos/me',
|
|
69
|
+
blockers: '/api/bongos/blockers?limit=100',
|
|
70
|
+
fence: '/api/bongos/autonomy/fence',
|
|
71
|
+
moving: '/api/bongos/moving',
|
|
72
|
+
};
|
|
39
73
|
|
|
40
74
|
var $ = function (id) { return document.getElementById(id); };
|
|
41
75
|
var media = window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)') : null;
|
|
42
|
-
var data = { docket: null, goals: [], rollups: [], loaded: false };
|
|
76
|
+
var data = { docket: null, goals: [], rollups: [], loaded: false, raw: {} };
|
|
77
|
+
var model = null;
|
|
78
|
+
var openKey = null;
|
|
79
|
+
var drewLiving = false;
|
|
43
80
|
|
|
44
81
|
// ---------- light / dark ----------
|
|
45
82
|
// A choice made on this page when storage is blocked (private mode) still
|
|
@@ -95,7 +132,9 @@
|
|
|
95
132
|
// (task 1004544); the full-screen switch resizes it and fires a resize.
|
|
96
133
|
var main = $('hall-main');
|
|
97
134
|
var w = (main && main.clientWidth) || window.innerWidth || 1440, h = (main && main.clientHeight) || window.innerHeight || 900;
|
|
98
|
-
|
|
135
|
+
// On a phone the city stands closer (task 1004685): the civic quarter fills the
|
|
136
|
+
// width and the far corners of the ground bleed off, as a plate would.
|
|
137
|
+
var s = Math.min(w < 640 ? w / 700 : w / 1000, h / 900);
|
|
99
138
|
stage.style.setProperty('--city-scale', String(Math.round(s * 1000) / 1000));
|
|
100
139
|
}
|
|
101
140
|
|
|
@@ -105,15 +144,50 @@
|
|
|
105
144
|
.then(function (r) { return r && r.ok ? r.json() : null; })
|
|
106
145
|
.catch(function () { return null; });
|
|
107
146
|
}
|
|
147
|
+
// Through gov-feeds.js when it is on the page: one read per feed per page view,
|
|
148
|
+
// shared with the shell's Governing tally, which therefore prints these counts.
|
|
149
|
+
function readFeeds() {
|
|
150
|
+
var F = window.GovFeeds;
|
|
151
|
+
var keys = ['docket', 'goals', 'rollups'];
|
|
152
|
+
if (window.GovLiving) Object.keys(LIVING_FEEDS).forEach(function (k) { keys.push(k); });
|
|
153
|
+
if (F && typeof F.read === 'function') return F.read(keys);
|
|
154
|
+
return Promise.all(keys.map(function (k) { return getJson(FEEDS[k] || LIVING_FEEDS[k]); })).then(function (res) {
|
|
155
|
+
var raw = {};
|
|
156
|
+
keys.forEach(function (k, i) { raw[k] = res[i]; });
|
|
157
|
+
return raw;
|
|
158
|
+
});
|
|
159
|
+
}
|
|
108
160
|
function load() {
|
|
109
|
-
return
|
|
110
|
-
data.
|
|
111
|
-
data.
|
|
112
|
-
data.
|
|
161
|
+
return readFeeds().then(function (raw) {
|
|
162
|
+
data.raw = raw;
|
|
163
|
+
data.docket = raw.docket && Array.isArray(raw.docket.matters) ? raw.docket : null;
|
|
164
|
+
data.goals = raw.goals && Array.isArray(raw.goals.goals) ? raw.goals.goals : [];
|
|
165
|
+
data.rollups = raw.rollups && Array.isArray(raw.rollups.rollups) ? raw.rollups.rollups : [];
|
|
166
|
+
data.living = livingOf(raw);
|
|
113
167
|
data.loaded = true;
|
|
114
168
|
draw(effectiveTheme());
|
|
115
169
|
});
|
|
116
170
|
}
|
|
171
|
+
// The living model (gov-living.js) over the same reads; null when the page has no model.
|
|
172
|
+
function livingOf(raw) {
|
|
173
|
+
var G = window.GovLiving;
|
|
174
|
+
if (!G || typeof G.livingModel !== 'function') return null;
|
|
175
|
+
if (window.GovFeeds) return window.GovFeeds.model(raw, Date.now());
|
|
176
|
+
try {
|
|
177
|
+
return G.livingModel({
|
|
178
|
+
docket: raw.docket, constitution: raw.constitution, goals: raw.goals, rollups: raw.rollups, blockers: raw.blockers,
|
|
179
|
+
fence: raw.fence, claims: raw.claims, tasks: raw.tasks, moving: raw.moving, cost: raw.cost, roster: raw.roster,
|
|
180
|
+
me: raw.me, laws: raw.laws, leads: raw.leads, counts: raw.counts, now: Date.now(),
|
|
181
|
+
});
|
|
182
|
+
} catch (_) { return null; }
|
|
183
|
+
}
|
|
184
|
+
// the counts this page prints: the one rule
|
|
185
|
+
function countsNow() {
|
|
186
|
+
var F = window.GovFeeds, L = window.GovLiving;
|
|
187
|
+
if (!data.living) return null;
|
|
188
|
+
if (F) return F.countsFrom(data.living);
|
|
189
|
+
return L ? L.countsOf(data.living) : null;
|
|
190
|
+
}
|
|
117
191
|
|
|
118
192
|
// ---------- drawing ----------
|
|
119
193
|
function clear(el) { while (el && el.firstChild) el.removeChild(el.firstChild); }
|
|
@@ -124,9 +198,19 @@
|
|
|
124
198
|
el.setAttribute('stroke', p.s);
|
|
125
199
|
if (p.dash && p.dash !== 'none') el.setAttribute('stroke-dasharray', p.dash);
|
|
126
200
|
el.setAttribute('stroke-width', String(p.w || 1));
|
|
201
|
+
if (p.cls) el.setAttribute('class', p.cls);
|
|
127
202
|
return el;
|
|
128
203
|
}
|
|
204
|
+
// A part of a building that moves (or stands at its still frame) is its own group.
|
|
205
|
+
function partEl(part) {
|
|
206
|
+
var g = document.createElementNS(SVG_NS, 'g');
|
|
207
|
+
if (part.cls) g.setAttribute('class', part.cls);
|
|
208
|
+
if (part.style) Object.keys(part.style).forEach(function (k) { g.style.setProperty(k, part.style[k]); });
|
|
209
|
+
part.polys.forEach(function (p) { g.appendChild(pathEl(p)); });
|
|
210
|
+
return g;
|
|
211
|
+
}
|
|
129
212
|
function span(cls) { var el = document.createElement('span'); el.className = cls; return el; }
|
|
213
|
+
function node(tag, cls, text) { var el = document.createElement(tag); if (cls) el.className = cls; if (text != null) el.textContent = String(text); return el; }
|
|
130
214
|
|
|
131
215
|
function drawPin(pin) {
|
|
132
216
|
var el = span('city-pin city-pin--' + pin.state);
|
|
@@ -140,6 +224,24 @@
|
|
|
140
224
|
var head = span('city-pin__head');
|
|
141
225
|
// only the urgent tier pulses; a settled pin is a plain grey dot, no mark (task 1004569)
|
|
142
226
|
if (pin.state === 'urgent') head.appendChild(span('city-pin__ring'));
|
|
227
|
+
// the age ring: how long the oldest ask on you has waited, full at two weeks (task 1004685)
|
|
228
|
+
if (pin.age != null) {
|
|
229
|
+
var ring = document.createElementNS(SVG_NS, 'svg');
|
|
230
|
+
ring.setAttribute('class', 'city-pin__age');
|
|
231
|
+
ring.setAttribute('viewBox', '0 0 32 32');
|
|
232
|
+
ring.setAttribute('aria-hidden', 'true');
|
|
233
|
+
var track = document.createElementNS(SVG_NS, 'circle');
|
|
234
|
+
track.setAttribute('cx', '16'); track.setAttribute('cy', '16'); track.setAttribute('r', '14');
|
|
235
|
+
track.setAttribute('class', 'city-pin__age-track');
|
|
236
|
+
var arc = document.createElementNS(SVG_NS, 'circle');
|
|
237
|
+
arc.setAttribute('cx', '16'); arc.setAttribute('cy', '16'); arc.setAttribute('r', '14');
|
|
238
|
+
arc.setAttribute('class', 'city-pin__age-arc');
|
|
239
|
+
arc.setAttribute('pathLength', '100');
|
|
240
|
+
arc.setAttribute('stroke-dasharray', Math.round(pin.age * 100) + ' 100');
|
|
241
|
+
ring.appendChild(track);
|
|
242
|
+
ring.appendChild(arc);
|
|
243
|
+
head.appendChild(ring);
|
|
244
|
+
}
|
|
143
245
|
if (pin.state !== 'settled') {
|
|
144
246
|
var svg = document.createElementNS(SVG_NS, 'svg');
|
|
145
247
|
svg.setAttribute('width', '14');
|
|
@@ -172,26 +274,333 @@
|
|
|
172
274
|
return el;
|
|
173
275
|
}
|
|
174
276
|
|
|
277
|
+
// one chrome pod per agent; its still frame stands at --pod-at along the lane
|
|
278
|
+
function drawPod(lane, pod) {
|
|
279
|
+
var el = span(lane.orbit ? 'city-pod city-pod--orbit' : 'city-pod');
|
|
280
|
+
el.setAttribute('data-lane', lane.key);
|
|
281
|
+
if (pod.task_id) el.setAttribute('data-task', pod.task_id);
|
|
282
|
+
el.style.offsetPath = "path('" + lane.d + "')";
|
|
283
|
+
el.style.setProperty('--pod-at', pod.at + '%');
|
|
284
|
+
el.style.animationDuration = pod.dur + 's';
|
|
285
|
+
el.style.animationDelay = pod.delay + 's';
|
|
286
|
+
return el;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function drawShaft(sh) {
|
|
290
|
+
var el = span('city-shaft');
|
|
291
|
+
el.setAttribute('data-building', sh.building);
|
|
292
|
+
el.style.left = sh.x + 'px';
|
|
293
|
+
el.style.top = (sh.y - sh.h) + 'px';
|
|
294
|
+
el.style.height = sh.h + 'px';
|
|
295
|
+
el.appendChild(span('city-shaft__ring'));
|
|
296
|
+
return el;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// A seated Lead's plate stands at rest; "owner" (a building nobody leads yet)
|
|
300
|
+
// shows only while its building is pointed at or focused, or its card is open,
|
|
301
|
+
// so five plates saying "owner" do not crowd the quarter (a class, no motion).
|
|
302
|
+
function showPlate(key, on) {
|
|
303
|
+
var sky = $('citySky');
|
|
304
|
+
var el = sky && sky.querySelector ? sky.querySelector('.city-plate--owner[data-building="' + key + '"]') : null;
|
|
305
|
+
if (!el) return;
|
|
306
|
+
var base = String(el.className || '').replace(/\s*\bcity-plate--shown\b/g, '');
|
|
307
|
+
el.className = on || openKey === key ? base + ' city-plate--shown' : base;
|
|
308
|
+
}
|
|
309
|
+
function hoverReveals(el, key) {
|
|
310
|
+
el.addEventListener('mouseenter', function () { showPlate(key, true); });
|
|
311
|
+
el.addEventListener('mouseleave', function () { showPlate(key, false); });
|
|
312
|
+
el.addEventListener('focus', function () { showPlate(key, true); });
|
|
313
|
+
el.addEventListener('blur', function () { showPlate(key, false); });
|
|
314
|
+
}
|
|
315
|
+
function drawPlate(c) {
|
|
316
|
+
var el = span(c.plate.lead ? 'city-plate' : 'city-plate city-plate--owner' + (openKey === c.key ? ' city-plate--shown' : ''));
|
|
317
|
+
el.setAttribute('data-building', c.key);
|
|
318
|
+
el.style.left = c.plate.x + 'px';
|
|
319
|
+
el.style.top = c.plate.y + 'px';
|
|
320
|
+
el.textContent = c.plate.text;
|
|
321
|
+
el.addEventListener('click', function () { openCard(c.key); });
|
|
322
|
+
return el;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// a count in the label every surface prints (gov-living.js COUNT_WORDS)
|
|
326
|
+
function countText(c) {
|
|
327
|
+
var L = window.GovLiving;
|
|
328
|
+
var w = function (k, n) { return L ? L.countWords(k, n) : k === 'on_you' ? 'to do' : 'wait on others'; };
|
|
329
|
+
return c.on_you ? c.on_you + ' ' + w('on_you', c.on_you) : c.on_others ? c.on_others + ' ' + w('on_others', c.on_others) : c.open ? c.open + ' moving' : 'nothing open';
|
|
330
|
+
}
|
|
331
|
+
// who holds it: a Lead, or the owner's seat (GET /government/leads); unknown names nobody
|
|
332
|
+
function hitLabel(c) {
|
|
333
|
+
var who = c.card.lead ? ', led by ' + c.card.plate : c.card.plate ? ', held by the owner' : '';
|
|
334
|
+
return c.name + who + ': ' + c.card.fact.text + ', ' + countText(c);
|
|
335
|
+
}
|
|
336
|
+
function drawHit(c) {
|
|
337
|
+
var el = node('button', 'city-hit');
|
|
338
|
+
el.setAttribute('type', 'button');
|
|
339
|
+
el.setAttribute('data-building', c.key);
|
|
340
|
+
el.setAttribute('aria-label', hitLabel(c));
|
|
341
|
+
el.setAttribute('aria-haspopup', 'dialog');
|
|
342
|
+
el.style.left = c.hit.x + 'px';
|
|
343
|
+
el.style.top = c.hit.y + 'px';
|
|
344
|
+
el.style.width = c.hit.w + 'px';
|
|
345
|
+
el.style.height = c.hit.h + 'px';
|
|
346
|
+
el.addEventListener('click', function () { openCard(c.key, el); });
|
|
347
|
+
hoverReveals(el, c.key);
|
|
348
|
+
return el;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function drawPlaque(tiles) {
|
|
352
|
+
var el = $('cityPlaque');
|
|
353
|
+
if (!el) return;
|
|
354
|
+
clear(el);
|
|
355
|
+
(tiles || []).forEach(function (t) {
|
|
356
|
+
var tile = node('span', 'city-plaque__tile');
|
|
357
|
+
tile.setAttribute('data-key', t.key);
|
|
358
|
+
tile.appendChild(node('span', 'city-plaque__figure', t.figure));
|
|
359
|
+
tile.appendChild(node('span', 'city-plaque__words', t.words));
|
|
360
|
+
el.appendChild(tile);
|
|
361
|
+
});
|
|
362
|
+
if (tiles && tiles.length) el.removeAttribute('hidden'); else el.setAttribute('hidden', '');
|
|
363
|
+
}
|
|
364
|
+
|
|
175
365
|
function draw(mode) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
366
|
+
model = City.cityModel({
|
|
367
|
+
docket: data.docket, goals: data.goals, rollups: data.rollups, mode: mode, now: Date.now(),
|
|
368
|
+
living: data.living || null, constitution: data.raw.constitution || null, counts: countsNow(),
|
|
369
|
+
});
|
|
370
|
+
var flat = $('cityFlat'), blds = $('cityBlds'), sky = $('citySkyLanes'), parcels = $('cityParcels'), pods = $('citySky'), pins = $('cityPins'), hits = $('cityHits');
|
|
371
|
+
clear(flat); clear(blds); clear(sky); clear(parcels); clear(pods); clear(pins); clear(hits);
|
|
179
372
|
model.flat.forEach(function (p) { flat.appendChild(pathEl(p)); });
|
|
373
|
+
var civicKeys = {};
|
|
374
|
+
(model.civic || []).forEach(function (c) { civicKeys[c.key] = true; });
|
|
180
375
|
model.blds.forEach(function (b) {
|
|
181
376
|
var g = document.createElementNS(SVG_NS, 'g');
|
|
182
377
|
g.setAttribute('data-key', b.key);
|
|
378
|
+
if (b.quiet) g.setAttribute('data-quiet', '');
|
|
379
|
+
// a pointer finds a building by its own painted faces, so the one in front wins
|
|
380
|
+
if (civicKeys[b.key]) { g.setAttribute('data-civic', b.key); g.addEventListener('click', function () { openCard(b.key); }); hoverReveals(g, b.key); }
|
|
183
381
|
b.polys.forEach(function (p) { g.appendChild(pathEl(p)); });
|
|
382
|
+
(b.parts || []).forEach(function (part) { g.appendChild(partEl(part)); });
|
|
184
383
|
blds.appendChild(g);
|
|
185
384
|
});
|
|
186
385
|
model.parcels.forEach(function (pc) { parcels.appendChild(drawParcel(pc)); });
|
|
187
|
-
model.
|
|
386
|
+
if (model.living) {
|
|
387
|
+
// the capsules plug in once, on the first living drawing; a redraw does not replay it
|
|
388
|
+
var st = $('cityStage');
|
|
389
|
+
if (drewLiving && st) st.setAttribute('data-drawn', '');
|
|
390
|
+
drewLiving = true;
|
|
391
|
+
if (sky) model.sky.forEach(function (p) { sky.appendChild(pathEl(p)); });
|
|
392
|
+
if (pods) {
|
|
393
|
+
model.shafts.forEach(function (sh) { pods.appendChild(drawShaft(sh)); });
|
|
394
|
+
model.lanes.forEach(function (L) {
|
|
395
|
+
L.pods.forEach(function (pod) { pods.appendChild(drawPod(L, pod)); });
|
|
396
|
+
// a lane past its convoy says how many more agents ride it
|
|
397
|
+
if (L.more && L.more.x != null) {
|
|
398
|
+
var more = span('city-lane__more');
|
|
399
|
+
more.textContent = '+' + L.more.n;
|
|
400
|
+
more.style.left = L.more.x + 'px';
|
|
401
|
+
more.style.top = L.more.y + 'px';
|
|
402
|
+
pods.appendChild(more);
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
model.civic.forEach(function (c) { if (!c.plate.hidden) pods.appendChild(drawPlate(c)); });
|
|
406
|
+
}
|
|
407
|
+
if (hits) model.civic.forEach(function (c) { hits.appendChild(drawHit(c)); });
|
|
408
|
+
}
|
|
409
|
+
model.pins.forEach(function (pin) {
|
|
410
|
+
var el = drawPin(pin);
|
|
411
|
+
if (model.living) el.firstChild.addEventListener('click', function () { openCard(pin.building); });
|
|
412
|
+
pins.appendChild(el);
|
|
413
|
+
});
|
|
188
414
|
var lineEl = $('cityLine');
|
|
189
415
|
if (lineEl && data.loaded) lineEl.textContent = model.line;
|
|
190
416
|
var stage = $('cityStage');
|
|
191
417
|
if (stage) stage.setAttribute('aria-label', model.label);
|
|
418
|
+
var start = $('cityStart');
|
|
419
|
+
if (start && model.cta) {
|
|
420
|
+
start.textContent = model.cta.label;
|
|
421
|
+
start.setAttribute('href', model.cta.href);
|
|
422
|
+
start.setAttribute('aria-label', model.cta.aria);
|
|
423
|
+
}
|
|
424
|
+
drawPlaque(model.plaque || null);
|
|
425
|
+
if (openKey) renderCard(openKey);
|
|
192
426
|
return model;
|
|
193
427
|
}
|
|
194
428
|
|
|
429
|
+
// ---------- the card a building opens in place (task 1004702) ----------
|
|
430
|
+
// A glass panel beside the building, over a light hush with the building left
|
|
431
|
+
// clear: its name, who holds it, one fact and what waits there; the building's
|
|
432
|
+
// first ask, answered in place (the owner's pick, 2026-10-13) through the same
|
|
433
|
+
// route the deck takes (GovDeck.sendAnswer: the desk row's own request); then
|
|
434
|
+
// two doors, "decide all N in a row" to the deck and "go inside" to the
|
|
435
|
+
// building on the street. Without the deck's scripts it is the row and doors.
|
|
436
|
+
var here = { done: {}, busy: false, said: '', room: null, roomAsked: false, cv: null };
|
|
437
|
+
function civicOf(key) { return model && model.civic ? model.civic.filter(function (c) { return c.key === key; })[0] || null : null; }
|
|
438
|
+
// The building's button: the keyboard's way in, and where the card is placed beside.
|
|
439
|
+
function hitFor(key) {
|
|
440
|
+
var hits = $('cityHits');
|
|
441
|
+
return hits && hits.querySelector ? hits.querySelector('[data-building="' + key + '"]') : null;
|
|
442
|
+
}
|
|
443
|
+
// the building's pin head, else its button: what the hairline and the halo find
|
|
444
|
+
function anchorFor(key) {
|
|
445
|
+
var pins = $('cityPins');
|
|
446
|
+
var pin = pins && pins.querySelector ? pins.querySelector('[data-building="' + key + '"]') : null;
|
|
447
|
+
return (pin && pin.firstChild) || hitFor(key);
|
|
448
|
+
}
|
|
449
|
+
function openCard(key, from) {
|
|
450
|
+
var was = openKey;
|
|
451
|
+
openKey = key;
|
|
452
|
+
if (was !== key) { here.said = ''; here.cv = null; }
|
|
453
|
+
if (was && was !== key) showPlate(was, false);
|
|
454
|
+
showPlate(key, true);
|
|
455
|
+
var el = renderCard(key);
|
|
456
|
+
if (!el) return;
|
|
457
|
+
place(el, from || hitFor(key));
|
|
458
|
+
var first = el.querySelector ? el.querySelector('.ask__opt, .city-card__go, .city-card__enter') : null;
|
|
459
|
+
if (first && first.focus) first.focus();
|
|
460
|
+
}
|
|
461
|
+
function closeCard() {
|
|
462
|
+
var el = $('cityCard');
|
|
463
|
+
var key = openKey;
|
|
464
|
+
openKey = null;
|
|
465
|
+
here.said = ''; here.cv = null;
|
|
466
|
+
if (key) showPlate(key, false);
|
|
467
|
+
if (el) { clear(el); el.setAttribute('hidden', ''); }
|
|
468
|
+
clear($('citySpot'));
|
|
469
|
+
var hits = $('cityHits');
|
|
470
|
+
if (key && hits && hits.querySelector) { var b = hits.querySelector('[data-building="' + key + '"]'); if (b && b.focus) b.focus(); }
|
|
471
|
+
}
|
|
472
|
+
// the building's first ask, as the deck draws it (needs-you first, then the oldest)
|
|
473
|
+
function firstAsk(key) {
|
|
474
|
+
var K = window.GovDeck;
|
|
475
|
+
if (!K || !K.drawAsk || !data.living || !data.docket) return null;
|
|
476
|
+
var q = K.queueOf(data.living, { done: here.done, delegated: {}, skipped: [], extra: [] }, key);
|
|
477
|
+
if (!q.length) return null;
|
|
478
|
+
var cv = K.cardView(q[0], { docket: data.docket, room: here.room, constitution: data.raw.constitution, goals: (data.goals || []).filter(function (g) { return g && g.status === 'open'; }), now: Date.now(), viewerId: data.docket.viewer ? String(data.docket.viewer.id) : null });
|
|
479
|
+
// a sitting's ballot is read once, when a vote's card first needs it
|
|
480
|
+
if (cv && cv.card && cv.card.loading && !here.roomAsked) {
|
|
481
|
+
here.roomAsked = true;
|
|
482
|
+
getJson('/api/bongos/government/board/items').then(function (r) {
|
|
483
|
+
here.room = r && (Array.isArray(r.open) || Array.isArray(r.closed)) ? r : { open: [], closed: [] };
|
|
484
|
+
if (openKey) renderCard(openKey);
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
return cv;
|
|
488
|
+
}
|
|
489
|
+
function sentence(s) { var t = String(s || '').trim(); return t ? t.charAt(0).toUpperCase() + t.slice(1) + (/[.!?]$/.test(t) ? '' : '.') : ''; }
|
|
490
|
+
function renderCard(key) {
|
|
491
|
+
var el = $('cityCard');
|
|
492
|
+
var c = civicOf(key);
|
|
493
|
+
if (!el || !c) return null;
|
|
494
|
+
var card = c.card, K = window.GovDeck;
|
|
495
|
+
clear(el);
|
|
496
|
+
el.removeAttribute('hidden');
|
|
497
|
+
el.className = 'city-card gov-glass';
|
|
498
|
+
el.setAttribute('data-building', key);
|
|
499
|
+
el.setAttribute('aria-label', card.name);
|
|
500
|
+
var head = node('div', 'city-card__head');
|
|
501
|
+
var top = node('div', 'city-card__top');
|
|
502
|
+
top.appendChild(node('p', 'city-card__name', card.name));
|
|
503
|
+
var close = node('button', 'city-card__close');
|
|
504
|
+
close.setAttribute('type', 'button');
|
|
505
|
+
close.setAttribute('aria-label', 'Close');
|
|
506
|
+
close.setAttribute('aria-keyshortcuts', 'Escape');
|
|
507
|
+
close.appendChild(node('kbd', null, 'esc'));
|
|
508
|
+
close.addEventListener('click', closeCard);
|
|
509
|
+
top.appendChild(close);
|
|
510
|
+
head.appendChild(top);
|
|
511
|
+
// who holds it, one fact, and what waits there (salmon only while it is yours)
|
|
512
|
+
var hold = K && K.holdWords ? K.holdWords(data.living, key) : card.plate ? (card.lead ? 'Led by ' + card.plate + '.' : 'The owner holds it until a Lead is seated.') : '';
|
|
513
|
+
var words = [hold, card.fact && card.fact.text ? sentence(card.fact.text) : ''].filter(Boolean).join(' ');
|
|
514
|
+
var line = node('p', 'city-card__f', words);
|
|
515
|
+
var waits = K && K.waitWords ? K.waitWords(card.on_you, card.on_others) : sentence(countText(card));
|
|
516
|
+
line.appendChild(node('span', card.on_you ? 'city-card__count city-card__count--you' : 'city-card__count', (words ? ' ' : '') + waits));
|
|
517
|
+
head.appendChild(line);
|
|
518
|
+
el.appendChild(head);
|
|
519
|
+
var cv = firstAsk(key);
|
|
520
|
+
here.cv = cv;
|
|
521
|
+
if (cv) el.appendChild(K.drawAsk(document, cv, { pick: function (op) { answer(key, cv, op); } }, { inner: true, busy: here.busy, cls: 'city-ask' }));
|
|
522
|
+
if (here.said) {
|
|
523
|
+
var said = node('p', 'city-card__said', here.said);
|
|
524
|
+
said.setAttribute('role', 'status');
|
|
525
|
+
el.appendChild(said);
|
|
526
|
+
}
|
|
527
|
+
var acts = node('div', 'city-card__acts');
|
|
528
|
+
var n = card.on_you || card.on_others;
|
|
529
|
+
if (n) {
|
|
530
|
+
var go = node('a', 'city-card__go', n === 1 ? 'decide it on the deck' : 'decide all ' + n + ' in a row');
|
|
531
|
+
go.setAttribute('href', card.deck_href);
|
|
532
|
+
acts.appendChild(go);
|
|
533
|
+
}
|
|
534
|
+
var enter = node('a', 'city-card__enter', 'go inside');
|
|
535
|
+
enter.setAttribute('href', card.enter_href);
|
|
536
|
+
acts.appendChild(enter);
|
|
537
|
+
el.appendChild(acts);
|
|
538
|
+
return el;
|
|
539
|
+
}
|
|
540
|
+
// One answer, through the deck's own sender: the same request its desk row sends.
|
|
541
|
+
function send(req) {
|
|
542
|
+
return fetch(req.url, { method: req.method, credentials: 'same-origin', headers: { accept: 'application/json', 'content-type': 'application/json' }, body: JSON.stringify(req.body || {}) })
|
|
543
|
+
.then(function (r) {
|
|
544
|
+
return Promise.resolve(r && r.json ? r.json() : {}).catch(function () { return {}; })
|
|
545
|
+
.then(function (b) { return { ok: !!(r && r.ok), status: r ? r.status : 0, body: b || {} }; });
|
|
546
|
+
})
|
|
547
|
+
.catch(function () { return { ok: false, status: 0, body: {} }; });
|
|
548
|
+
}
|
|
549
|
+
function answer(key, cv, op) {
|
|
550
|
+
var K = window.GovDeck;
|
|
551
|
+
if (!K || here.busy || !op) return Promise.resolve(false);
|
|
552
|
+
if (op.plan && op.plan.mode === 'record') { here.said = 'This one is signed off with a recording: decide it on the deck.'; renderCard(key); return Promise.resolve(false); }
|
|
553
|
+
here.busy = true;
|
|
554
|
+
renderCard(key);
|
|
555
|
+
return K.sendAnswer(op, send).then(function (res) {
|
|
556
|
+
here.busy = false;
|
|
557
|
+
here.said = res.toast;
|
|
558
|
+
if (res.ok) {
|
|
559
|
+
here.done[cv.id] = true;
|
|
560
|
+
// every count on the page leaves out what was answered here (gov-feeds.js)
|
|
561
|
+
if (window.GovFeeds && window.GovFeeds.changed) window.GovFeeds.changed();
|
|
562
|
+
}
|
|
563
|
+
if (res.ok && res.stored) return load().then(function () { return true; });
|
|
564
|
+
if (openKey) { draw(painted || effectiveTheme()); var f = $('cityCard'); var first = f && f.querySelector ? f.querySelector('.ask__opt, .city-card__go') : null; if (first && first.focus) first.focus(); }
|
|
565
|
+
return res.ok;
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
// 1 / 2 / 3 answer the card's ask, as on the deck
|
|
569
|
+
function cardKey(e) {
|
|
570
|
+
if (!e) return;
|
|
571
|
+
if (e.key === 'Escape') { closeCard(); return; }
|
|
572
|
+
if (!here.cv || !openKey || e.ctrlKey || e.metaKey || e.altKey || !/^[1-3]$/.test(String(e.key))) return;
|
|
573
|
+
var op = here.cv.options[Number(e.key) - 1];
|
|
574
|
+
if (!op) return;
|
|
575
|
+
if (e.preventDefault) e.preventDefault();
|
|
576
|
+
answer(openKey, here.cv, op);
|
|
577
|
+
}
|
|
578
|
+
// Beside the building on a wide stage, over a light hush with the building
|
|
579
|
+
// clear and a hairline to its pin; a sheet along the foot on a phone (city.css).
|
|
580
|
+
function place(el, from) {
|
|
581
|
+
var main = $('hall-main');
|
|
582
|
+
if (!from || !main || typeof from.getBoundingClientRect !== 'function' || typeof main.getBoundingClientRect !== 'function') return;
|
|
583
|
+
var narrow = window.matchMedia ? window.matchMedia('(max-width: 640px)').matches : false;
|
|
584
|
+
if (narrow) { el.style.left = ''; el.style.top = ''; }
|
|
585
|
+
var K = window.GovDeck, layer = $('citySpot');
|
|
586
|
+
if (!layer && K && K.spot) {
|
|
587
|
+
layer = node('div', 'gov-spot city-spot');
|
|
588
|
+
layer.setAttribute('id', 'citySpot');
|
|
589
|
+
layer.setAttribute('aria-hidden', 'true');
|
|
590
|
+
main.appendChild(layer);
|
|
591
|
+
}
|
|
592
|
+
var c = civicOf(openKey);
|
|
593
|
+
if (K && K.spot && layer) { K.spot(document, layer, main, anchorFor(openKey) || from, el, { place: narrow ? null : el, top: 62, you: !!(c && c.card.on_you) }); return; }
|
|
594
|
+
if (narrow) return;
|
|
595
|
+
var r = from.getBoundingClientRect(), m = main.getBoundingClientRect();
|
|
596
|
+
var w = el.offsetWidth || 360, h = el.offsetHeight || 320;
|
|
597
|
+
var left = r.right - m.left + 14;
|
|
598
|
+
if (left + w > m.width - 16) left = r.left - m.left - w - 14;
|
|
599
|
+
var top = Math.max(76, Math.min(r.top - m.top - 12, m.height - h - 16));
|
|
600
|
+
el.style.left = Math.max(16, Math.round(left)) + 'px';
|
|
601
|
+
el.style.top = Math.round(top) + 'px';
|
|
602
|
+
}
|
|
603
|
+
|
|
195
604
|
function boot() {
|
|
196
605
|
var start = $('cityStart');
|
|
197
606
|
if (start) start.setAttribute('href', City.STREET_HREF);
|
|
@@ -199,6 +608,17 @@
|
|
|
199
608
|
if (btn) btn.addEventListener('click', toggleTheme);
|
|
200
609
|
if (media && media.addEventListener) media.addEventListener('change', function () { if (!storedTheme()) draw(applyTheme()); });
|
|
201
610
|
window.addEventListener('resize', fit);
|
|
611
|
+
var card = $('cityCard');
|
|
612
|
+
if (card) card.addEventListener('keydown', cardKey);
|
|
613
|
+
// the page's counts leave out what the card answered (gov-feeds.js)
|
|
614
|
+
if (window.GovFeeds && typeof window.GovFeeds.useSession === 'function') window.GovFeeds.useSession(function () { return { answered: Object.keys(here.done), extra: [] }; });
|
|
615
|
+
var frame = $('cityFrame');
|
|
616
|
+
// a click anywhere on the city but a building (its faces, its pin, its plate) closes the card
|
|
617
|
+
if (frame) frame.addEventListener('click', function (e) {
|
|
618
|
+
var t = e && e.target;
|
|
619
|
+
if (!openKey || (t && t.closest && t.closest('.city-hit, [data-civic], .city-pin__head, .city-plate'))) return;
|
|
620
|
+
closeCard();
|
|
621
|
+
});
|
|
202
622
|
fit();
|
|
203
623
|
painted = applyTheme();
|
|
204
624
|
draw(painted);
|
|
@@ -206,6 +626,6 @@
|
|
|
206
626
|
return load();
|
|
207
627
|
}
|
|
208
628
|
|
|
209
|
-
window.GovCityPage = { boot: boot, draw: draw, load: load, fit: fit, FEEDS: FEEDS, THEME_KEY: THEME_KEY };
|
|
629
|
+
window.GovCityPage = { boot: boot, draw: draw, load: load, fit: fit, openCard: openCard, closeCard: closeCard, FEEDS: FEEDS, LIVING_FEEDS: LIVING_FEEDS, THEME_KEY: THEME_KEY };
|
|
210
630
|
window.GovCityPage.ready = boot();
|
|
211
631
|
}());
|