/* * Aquarium — XZBT Exhibit Contract 5.3 adapter. * * This file is the only place in the exhibit that knows the contract exists. * It does three things and nothing else: * * 1. declares the public target catalog against the exhibit's real model * 2. binds each target to a real exhibit service (setter / reader / action) * 3. routes the native UI through the canonical mutation path * * It holds no state of its own. Every value it reports is read live from the * exhibit, so there is no shadow copy that can drift (Authoring Guide §W). */ (function () { 'use strict'; var Core = window.XZBTContractCore; var IDENTITY = { product: 'Aquarium', version: '1.0.0', build: 'reference-exhibit' }; /* ------------------------------------------------------------------ * * Public target catalog. * * IDs are canonical and domain-shaped. They are not DOM ids, CSS * selectors, or widget names (Authoring Guide §W). * ------------------------------------------------------------------ */ var DESCRIPTORS = [ { id: 'tank.species', kind: 'selection', label: 'Species', description: 'The species currently stocked in the tank.', options: [ { value: 'clownfish', label: 'Clownfish' }, { value: 'blue-tang', label: 'Blue Tang' }, { value: 'neon-tetra', label: 'Neon Tetra' }, { value: 'angelfish', label: 'Angelfish' }, { value: 'guppy', label: 'Guppy' } ], readable: true, writable: true, restorable: true, category: 'tank', requires: [] }, { id: 'tank.population', kind: 'range', label: 'Population', description: 'Number of fish in the tank.', min: 1, max: 40, step: 1, unit: 'fish', readable: true, writable: true, restorable: true, category: 'tank', requires: [] }, { id: 'tank.lighting', kind: 'selection', label: 'Lighting', description: 'Lighting programme for the tank.', options: [ { value: 'daylight', label: 'Daylight' }, { value: 'twilight', label: 'Twilight' }, { value: 'moonlight', label: 'Moonlight' }, { value: 'blackwater', label: 'Blackwater' } ], readable: true, writable: true, restorable: true, category: 'tank', requires: [] }, { id: 'tank.current', kind: 'range', label: 'Water current', description: 'Strength of the circulation pump, 0 (still) to 1 (strong).', min: 0, max: 1, step: 0.01, readable: true, writable: true, restorable: true, category: 'tank', requires: [] }, { id: 'tank.temperature', kind: 'range', label: 'Temperature', description: 'Heater setting, 0 (18 C) to 1 (28 C).', min: 0, max: 1, step: 0.01, readable: true, writable: true, restorable: true, category: 'tank', requires: [] }, { id: 'tank.paused', kind: 'state', valueType: 'boolean', label: 'Paused', description: 'Whether the simulation is frozen.', readable: true, writable: true, restorable: true, category: 'tank', requires: [] }, { id: 'action.feed', kind: 'impulse', label: 'Feed', description: 'Scatter food across the surface.', readable: false, writable: false, restorable: false, category: 'action', requires: [] }, { id: 'action.startle', kind: 'impulse', label: 'Startle', description: 'Send a pressure wave through the tank.', readable: false, writable: false, restorable: false, category: 'action', requires: [] }, { id: 'action.clean-glass', kind: 'impulse', label: 'Clean glass', description: 'Run the glass-cleaning cycle.', readable: false, writable: false, restorable: false, category: 'action', requires: [] }, { id: 'telemetry.fish-count', kind: 'state', valueType: 'number', label: 'Fish count', description: 'Live number of fish currently rendered.', readable: true, writable: false, restorable: false, category: 'telemetry', requires: [] }, { id: 'telemetry.food-level', kind: 'state', valueType: 'number', label: 'Food level', description: 'Remaining food in the water column, 0 to 1.', readable: true, writable: false, restorable: false, category: 'telemetry', requires: [] }, { id: 'telemetry.water-temp', kind: 'state', valueType: 'number', unit: 'C', label: 'Water temperature', description: 'Actual water temperature in degrees Celsius.', readable: true, writable: false, restorable: false, category: 'telemetry', requires: [] } ]; /* ------------------------------------------------------------------ * * Wiring * ------------------------------------------------------------------ */ function createAquariumContract(exhibit, ui) { var catalog = new Core.Catalog(DESCRIPTORS); /* The exhibit has no capability that can fail: it is pure canvas and * needs no device, no network, and no permission. Reporting `ready` * once is honest; inventing loading/error transitions would not be * (Authoring Guide §M). */ var capabilities = new Core.CapabilityRegistry(); capabilities.declare('render', 'ready'); var setters = { 'tank.species': function (v) { return exhibit.setSpecies(v); }, 'tank.population': function (v) { return exhibit.setPopulation(v); }, 'tank.lighting': function (v) { return exhibit.setLighting(v); }, 'tank.current': function (v) { return exhibit.setCurrent(v); }, 'tank.temperature': function (v) { return exhibit.setTemperature(v); }, 'tank.paused': function (v) { return exhibit.setPaused(v); } }; var readers = {}; var ids = catalog.ids(); for (var i = 0; i < ids.length; i++) { (function (id) { readers[id] = function () { return exhibit.read(id); }; })(ids[i]); } var actions = { 'action.feed': function () { return exhibit.feed(); }, 'action.startle': function () { return exhibit.startle(); }, 'action.clean-glass': function () { return exhibit.cleanGlass(); } }; var core = new Core.ContractCore({ identity: IDENTITY, catalog: catalog, capabilities: capabilities, setters: setters, readers: readers, actions: actions }); /* Route the native UI through the canonical path. From here on, a * slider drag and a host `set` are the same transaction, and both are * reported with the same source semantics (Authoring Guide §H). */ ui.dispatchOverride = function (targetId, value) { core.applyMutation(targetId, value, 'ui'); ui.sync(); }; ui.invokeOverride = function (targetId) { core.invokeAction(targetId, {}, 'ui'); ui.sync(); }; /* Keep the controls honest when the exhibit changes for any reason. */ exhibit.onChange = function () { ui.sync(); }; return core; } window.AquariumContract = { create: createAquariumContract, IDENTITY: IDENTITY }; })();