1/* In-memory adapter for the shared setup wizard. Never performs network I/O. */ 2(() => { 3 "use strict"; 4 if (!window.dccDemoMode) return; 5 const snapshot = window.dccDemoRadioCatalog; 6 if (!snapshot) throw new Error("The offline Hamlib catalog is missing"); 7 const clone = value => JSON.parse(JSON.stringify(value)); 8 const audio = ["DEMO_RADIO", "DEMO_INTERFACE"].map((device_id, index) => ({ 9 device_id, label: index ? "Sample external audio interface" : "Sample radio USB audio", 10 capture_device: `plughw:CARD=${device_id},DEV=0`, playback_device: `plughw:CARD=${device_id},DEV=0`, 11 likely_radio_audio: true 12 })); 13 let current = {model_id: 3085, connection_type: "serial", rig_port: "/dev/ttyUSB0", 14 baud: "auto", ptt: "RIG", hamlib_settings: {}, audio_device: audio[0].capture_device, 15 playback_device: audio[0].playback_device}; 16 const saved = window.dccReadDemoSetting?.("radio-configuration", null); 17 if (saved && snapshot.models.some(model => model.model_id === saved.model_id)) current = {...current, ...saved}; 18 let status = {state: "idle", schema_version: 2, simulated: true}; 19 20 function validate(request) { 21 const model = snapshot.models.find(item => item.model_id === request.model_id); 22 if (!model) throw new Error("Select a radio from the Hamlib catalog"); 23 if (!request.rig_port?.trim()) throw new Error("Enter a sample CAT device or network address"); 24 if (["network", "rigctld"].includes(request.connection_type) && !/^(?:[\w.-]+|\[[\da-f:.]+\]):\d{1,5}$/i.test(request.rig_port)) { 25 throw new Error("Use host:port or [IPv6]:port for a sample network connection"); 26 } 27 const options = snapshot.details[String(request.model_id)]?.settings || []; 28 for (const [key, value] of Object.entries(request.hamlib_settings || {})) { 29 const option = options.find(item => item.name === key && item.editable); 30 if (!option) throw new Error(`Unsupported setting: ${key}`); 31 if (!value || /[\s,\x00]/.test(value) || new TextEncoder().encode(value).length > 99) throw new Error(`Invalid value for ${key}`); 32 if (option.type === "combo" && !option.choices.includes(value)) throw new Error(`Choose a supported value for ${key}`); 33 if (option.type === "number") { 34 const number = Number(value), step = Number(option.step); 35 if (!Number.isFinite(number) || number < Number(option.minimum) || number > Number(option.maximum) || 36 (step > 0 && Math.abs((number - Number(option.minimum)) / step - Math.round((number - Number(option.minimum)) / step)) > 0.000001)) { 37 throw new Error(`${key} must be ${option.minimum}â${option.maximum}, step ${option.step}`); 38 } 39 } 40 } 41 if (!audio.some(item => item.capture_device === request.audio_device)) throw new Error("Select a sample receive audio device"); 42 if (!audio.some(item => item.playback_device === request.playback_device)) throw new Error("Select a sample playback audio device"); 43 return model; 44 } 45 46 window.dccDemoRadioApi = async (url, options = {}) => { 47 const route = String(url).split("?")[0]; 48 if (route === "/api/v1/radio/setup/catalog") return clone({...snapshot, details: undefined, 49 serial_candidates: [{path: "/dev/ttyUSB0", likely_model: "Sample CAT interface", stable_path: false}], 50 audio_candidates: audio, suggestions: [], simulated: true}); 51 if (route === "/api/v1/radio/detect") { 52 const selected = snapshot.models.find(model => model.display_name === window.dccDemoStation?.profile.radio); 53 if (selected && selected.model_id !== current.model_id) current = {...current, model_id: selected.model_id, hamlib_settings: {}}; 54 return clone({configured: current, connected: true, hardware_connected: false, simulated: true}); 55 } 56 const match = route.match(/^\/api\/v1\/radio\/setup\/models\/(\d+)$/); 57 if (match && snapshot.details[match[1]]) return clone(snapshot.details[match[1]]); 58 if (route === "/api/v1/radio/setup/status") return clone(status); 59 if (options.method === "POST" && ["/api/v1/radio/setup/probe", "/api/v1/radio/setup/apply"].includes(route)) { 60 const request = JSON.parse(options.body); 61 const model = validate(request); 62 if (route.endsWith("/probe")) return {simulated: true, ready_to_apply: true,
63 connection_detail: "Demo input validation only â hardware is not tested", checks: { 64 model_available: true, settings_valid: true, connection_present: true, 65 audio_device_detected: true, playback_device_detected: true, current_cat_connected: true 66 }, transmit_locked: false, ptt_test_performed: false}; 67 current = {...clone(request), ptt: request.ptt_type}; 68 window.localStorage?.setItem("radio-configuration", JSON.stringify(current)); 69 const requestedAt = new Date().toISOString(); 70 status = {state: "success", schema_version: 2, simulated: true, requested_at_utc: requestedAt, 71 radio_name: model.display_name, transmit_locked: false}; 72 window.dispatchEvent?.(new CustomEvent("dcc-demo-radio-applied", {detail: {radio: model.display_name}})); 73 return {queued: true, requested_at_utc: requestedAt, simulated: true}; 74 } 75 throw new Error("This operation is unavailable in the offline demo"); 76 }; 77})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.