1/** 2 * tec-desc-spaziale.js â Modalità Descrizione Spaziale (MDS) Phase 1 3 * KB suggestion + override tecnologo; gating Tab 3 3D + DVR §1.3.1. 4 */ 5(function (root) { 6 'use strict'; 7 if (typeof root === 'undefined') return; 8 9 var VERSION = '1.1.0'; 10 11 var MODALITA = { 12 NARRATIVA: 'narrativa', 13 MONOPIANO_GUIDATA: 'monopiano_guidata', 14 MULTIPIANO_GUIDATA: 'multipiano_guidata', 15 IBRIDA: 'ibrida' 16 }; 17 18 var MODALITA_LABELS = { 19 narrativa: 'Narrativa â §1.3 testuale', 20 monopiano_guidata: 'Monopiano guidata â elenco compatto §1.3.1', 21 multipiano_guidata: 'Multipiano guidata â tabella 3D per piano', 22 ibrida: 'Ibrida â narrativa + tabella 3D' 23 }; 24 25 function _ni(v, def) { 26 var n = parseInt(v, 10); 27 return isNaN(n) ? (def == null ? 0 : def) : n; 28 } 29 30 function _normalizeAteco(d) { 31 return String((d && d.atecoPrimario) || '').replace(/[\s.R\/]/g, ''); 32 } 33 34 function _hotelArch() { 35 return root._tecHotelLuoghiArch || null; 36 } 37 38 function _edificioStruttura(d) { 39 d = d || {}; 40 var H = _hotelArch(); 41 if (H && typeof H.getEdificioStrutturaFields === 'function') { 42 return H.getEdificioStrutturaFields(d, d.ambientiSede || []); 43 } 44 return { nPianiFT: d.nPianiFT, nPianiInterrati: d.nPianiInterrati }; 45 } 46 47 function _nPianiFT(d) { 48 var es = _edificioStruttura(d || {}); 49 return _ni(es.nPianiFT, 1) || 1; 50 } 51 52 function _nPianiInterrati(d) { 53 var es = _edificioStruttura(d || {}); 54 return _ni(es.nPianiInterrati, 0); 55 } 56 57 function _luogoForm() { 58 return root._tecLuogoTipoForm || null; 59 } 60 61 function _isSportWakeboard(d) { 62 return String((d && d.tipologiaAzienda) || '').toLowerCase() === 'sport_wakeboard'; 63 } 64 65 function _hasOutdoorAquaticAmbiente(d) { 66 var L = _luogoForm(); 67 var ambs = (d && d.ambientiSede) || []; 68 var i; 69 for (i = 0; i < ambs.length; i++) { 70 var amb = ambs[i] || {}; 71 var tipo = amb.tipo || amb.tipoArea || ''; 72 if (L && typeof L.isOutdoorAquaticProfile === 'function' && L.isOutdoorAquaticProfile(tipo)) return true; 73 var car = amb.caratteristiche || []; 74 if (car.indexOf('acquatico') >= 0 || car.indexOf('esterno') >= 0) return true; 75 } 76 return false; 77 } 78 79 function _hasBuildingProfile(d) { 80 var L = _luogoForm(); 81 if (L && typeof L.getProfileForTipo === 'function') { 82 var ambs = (d && d.ambientiSede) || []; 83 var i; 84 for (i = 0; i < ambs.length; i++) { 85 if (L.getProfileForTipo(ambs[i] && ambs[i].tipo) === 'building') return true; 86 } 87 } 88 if (d && d.tipoEdificio) return true; 89 return false; 90 } 91 92 function _hasResortOutdoorMix(d) { 93 var L = _luogoForm(); 94 if (!L || typeof L.getProfileForTipo !== 'function') return false; 95 var hasOutdoor = false; 96 var hasBuilding = false; 97 ((d && d.ambientiSede) || []).forEach(function (amb) { 98 var p = L.getProfileForTipo(amb && amb.tipo); 99 if (p === 'outdoor' || p === 'aquatic') hasOutdoor = true; 100 if (p === 'building') hasBuilding = true; 101 }); 102 if (!hasOutdoor || !hasBuilding) return false; 103 var at = _normalizeAteco(d); 104 if (at.indexOf('55') === 0 || at.indexOf('56') === 0) return true; 105 return hasOutdoor && hasBuilding; 106 } 107 108 function suggestModalita(d) { 109 d = d || {}; 110 var ateco = _normalizeAteco(d); 111 var nFt = _nPianiFT(d); 112 var nInt = _nPianiInterrati(d); 113 114 if (/cantiere_edile|impresa_edile|edilizia/i.test(String(d.tipologiaAzienda || d.settore || '')) 115 || ateco.indexOf('41') === 0 || ateco.indexOf('42') === 0 || ateco.indexOf('43') === 0) { 116 return { 117 modalita: MODALITA.NARRATIVA, 118 suggestedBy: 'kb:ateco_edilizia_cantiere', 119 perche: 'Impresa edile / general contractor â sede fissa + cantieri temporanei (§1.3 narrativo, non hotel).' 120 }; 121 } 122 123 if (_isSportWakeboard(d)) { 124 return { 125 modalita: MODALITA.NARRATIVA, 126 suggestedBy: 'kb:tipologia_sport_wakeboard', 127 perche: 'Profilo sport acquatico / wakeboard â descrizione narrativa §1.3.' 128 }; 129 } 130 131 if (_hasOutdoorAquaticAmbiente(d)) { 132 var L0 = _luogoForm(); 133 if (L0) { 134 var ambs0 = d.ambientiSede || []; 135 var j; 136 for (j = 0; j < ambs0.length; j++) {
137 if (L0.isOutdoorAquaticProfile(ambs0[j] && ambs0[j].tipo)) { 138 return { 139 modalita: MODALITA.NARRATIVA, 140 suggestedBy: 'kb:outdoor_aquatic_profile', 141 perche: 'Ambiente outdoor/acquatico â priorità a §1.3 narrativo.' 142 }; 143 } 144 } 145 } 146 } 147 148 if (ateco.indexOf('9311') === 0 && _hasOutdoorAquaticAmbiente(d)) { 149 return { 150 modalita: MODALITA.NARRATIVA, 151 suggestedBy: 'kb:ateco_9311_outdoor', 152 perche: 'ATECO 93.11 con aree outdoor â modalità narrativa.' 153 }; 154 } 155 156 if (ateco.indexOf('55') === 0 && nFt >= 2) { 157 return { 158 modalita: MODALITA.MULTIPIANO_GUIDATA, 159 suggestedBy: 'kb:ateco_55_hotel', 160 perche: 'Struttura alberghiera multipiano â tabella 3D per piano.' 161 }; 162 } 163 164 if (ateco.indexOf('85') === 0) { 165 return { 166 modalita: MODALITA.MULTIPIANO_GUIDATA, 167 suggestedBy: 'kb:ateco_85_school', 168 perche: 'Edificio scolastico â distribuzione per piano.' 169 }; 170 } 171 172 if (_hasResortOutdoorMix(d)) { 173 return { 174 modalita: MODALITA.IBRIDA, 175 suggestedBy: 'kb:resort_outdoor_mix', 176 perche: 'Mix edificio + aree outdoor â narrativa e tabella 3D.' 177 }; 178 } 179 180 if (nFt <= 1 && nInt <= 0 && _hasBuildingProfile(d)) { 181 return { 182 modalita: MODALITA.MONOPIANO_GUIDATA, 183 suggestedBy: 'kb:monopiano_building', 184 perche: 'Monopiano edificio â elenco ambienti compatto §1.3.1.' 185 }; 186 } 187 188 if (nFt <= 1) { 189 return { 190 modalita: MODALITA.MONOPIANO_GUIDATA, 191 suggestedBy: 'kb:fallback_monopiano', 192 perche: 'Un solo piano fuori terra â default monopiano guidata.' 193 }; 194 } 195 196 return { 197 modalita: MODALITA.MULTIPIANO_GUIDATA, 198 suggestedBy: 'kb:fallback_multipiano', 199 perche: 'Più piani fuori terra â default multipiano guidata.' 200 }; 201 } 202 203 function getModalita(d) { 204 d = d || {}; 205 if (d.descSpaziale && d.descSpaziale.modalita) return d.descSpaziale.modalita; 206 return suggestModalita(d).modalita; 207 } 208 209 function shouldShowSez131(d) { 210 d = d || {}; 211 var m = getModalita(d); 212 var ds = d.descSpaziale || {}; 213 if (m === MODALITA.NARRATIVA && ds.skipSez131) return false; 214 return true; 215 } 216 217 function shouldShowTab3D(d) { 218 return getModalita(d) !== MODALITA.NARRATIVA; 219 } 220 221 function shouldShowNarrativaNote(d) { 222 var m = getModalita(d); 223 return m === MODALITA.NARRATIVA || m === MODALITA.IBRIDA; 224 } 225 226 function getSez3DLabel(d) { 227 var m = getModalita(d); 228 if (m === MODALITA.NARRATIVA) return null; 229 if (m === MODALITA.MONOPIANO_GUIDATA) return 'Zone e ambienti'; 230 return '3D â Distribuzione locali per piano'; 231 } 232 233 function _esc(s) { 234 return String(s == null ? '' : s) 235 .replace(/&/g, '&') 236 .replace(/</g, '<') 237 .replace(/"/g, '"'); 238 } 239 240 function renderConfiguratorCard(d) { 241 d = d || {}; 242 var cur = getModalita(d); 243 var sug = suggestModalita(d); 244 var ds = d.descSpaziale || {}; 245 var skip = !!ds.skipSez131; 246 var showSkip = cur === MODALITA.NARRATIVA || cur === MODALITA.MONOPIANO_GUIDATA; 247 var opts = Object.keys(MODALITA_LABELS).map(function (k) { 248 return '<option value="' + k + '"' + (cur === k ? ' selected' : '') + '>' + _esc(MODALITA_LABELS[k]) + '</option>'; 249 }).join(''); 250 251 return '<div id="tec-mds-card" style="background:linear-gradient(135deg,#f0f9ff,#eff6ff);border:1.5px solid #93c5fd;border-radius:14px;padding:14px 18px;margin-bottom:14px">' 252 + '<div style="display:flex;align-items:flex-start;gap:12px;flex-wrap:wrap">' 253 + '<span style="font-size:22px;line-height:1">ðºï¸</span>' 254 + '<div style="flex:1;min-width:220px">' 255 + '<p style="font-size:13px;font-weight:800;color:#1e3a8a;margin:0 0 4px">Modalità Descrizione Spaziale</p>' 256 + '<p style="font-size:11px;color:#475569;margin:0 0 10px;line-height:1.45">Suggerimento KB: <code style="font-size:10px;background:#dbeafe;padding:1px 6px;border-radius:4px">' + _esc(sug.suggestedBy) + '</code> â ' + _esc(sug.perche) + '</p>' 257 + '<label class="fl" style="font-size:10px;margin-bottom:4px">Modalità per questo DVR</label>' 258 + '<select id="tec-mds-modalita" data-tec-focus-key="tec-mds-modalita" onchange="window._tecDescSpaziale&&window._tecDescSpaziale._onModalitaChange(this)" style="width:100%;max-width:420px;border:1.5px solid #93c5fd;border-radius:8px;padding:8px 10px;font-size:12px;font-weight:600;background:#fff">' 259 + opts 260 + '</select>' 261 + (showSkip 262 ? '<label style="display:flex;align-items:center;gap:8px;margin-top:10px;font-size:11px;color:#334155;cursor:pointer">' 263 + '<input type="checkbox" id="tec-mds-skip131"' + (skip ? ' checked' : '') + ' onchange="window._tecDescSpaziale&&window._tecDescSpaziale._onSkip131Change(this)">' 264 + 'Ometti §1.3.1 nel DVR</label>' 265 : '') 266 + (ds.decidedAt ? '<p style="font-size:9px;color:#94a3b8;margin:8px 0 0">Deciso: ' + _esc(ds.decidedAt) + '</p>' : '') 267 + '</div></div></div>'; 268 } 269 270 function _pianiBodyHtml(lv) { 271 lv = lv || function () { return []; }; 272 var piani = lv('piani') || []; 273 var rows = piani.map(function (p, i) { 274 var nome = p.nome || ('Piano ' + (i === 0 ? 'Terra' : i)); 275 var locali = (p.locali || []).length ? (p.locali || []).join(', ') : 'â'; 276 return '<div class="border rounded-xl p-4 bg-slate-50">' 277 + '<div class="mb-2"><span class="text-[10px] font-bold text-slate-400 uppercase tracking-wider">' + _esc(nome) + '</span></div>' 278 + '<div class="grid grid-cols-3 gap-3 mb-3 text-xs">' 279 + '<div><label class="fl">Nome piano</label><div style="padding:6px 8px;background:#f8fafc;border-radius:6px">' + _esc(p.nome || 'â') + '</div></div>' 280 + '<div><label class="fl">Destinazione</label><div style="padding:6px 8px;background:#f8fafc;border-radius:6px">' + _esc(p.destinazione || 'â') + '</div></div>' 281 + '<div><label class="fl">Note</label><div style="padding:6px 8px;background:#f8fafc;border-radius:6px">' + _esc(p.note || 'â') + '</div></div>' 282 + '</div>' 283 + '<div class="mb-2"><label class="fl mb-1">Locali presenti</label>' 284 + '<div style="font-size:11px;color:#475569">' + _esc(locali) + '</div></div></div>'; 285 }).join(''); 286 var empty = piani.length === 0 287 ? '<p class="text-sm text-amber-600 text-center py-4 bg-amber-50 rounded-xl">â ï¸ Nessun piano sul Luogo Principale â aggiungili in Tab 4 Luoghi</p>' 288 : ''; 289 return '<p class="text-xs text-slate-400 mb-3">Piani dal Luogo Principale (sola lettura).</p><div class="space-y-3">' + rows + empty + '</div>'; 290 } 291 292 function renderPiani3DSection(d, ctx) { 293 d = d || {}; 294 ctx = ctx || {}; 295 var SEC = ctx.SEC; 296 if (!SEC || typeof SEC !== 'function') return ''; 297 298 var parts = []; 299 if (shouldShowNarrativaNote(d)) { 300 var m = getModalita(d); 301 parts.push('<div style="background:#f8fafc;border:1.5px solid #e2e8f0;border-radius:12px;padding:12px 16px;margin-bottom:12px;font-size:12px;color:#475569">' 302 + '<i class="fas fa-align-left" style="color:#64748b;margin-right:6px"></i>' 303 + '<strong>§1.3 Descrizione ambienti</strong> â fonte primaria per la descrizione spaziale' 304 + (m === MODALITA.IBRIDA ? '; la tabella 3D resta disponibile sotto.' : ' (sezione 3D non ric
304hiesta in questa modalità ).') 305 + '</div>'); 306 } 307 308 if (!shouldShowTab3D(d)) return parts.join(''); 309 310 var icons = ctx.sectorIcons || {}; 311 var sectorKey = ctx.sectorKey || 'generic'; 312 var icon = icons[sectorKey] || 'ð¢'; 313 var label = getSez3DLabel(d) || '3D â Distribuzione locali per piano'; 314 var lv = ctx.lv || function () { return []; }; 315 316 parts.push(SEC('piani', icon, label, _pianiBodyHtml(lv))); 317 return parts.join(''); 318 } 319 320 function applyModalita(modalita, skipSez131, onChange) { 321 var d = typeof root.D === 'function' ? root.D() : {}; 322 var sug = suggestModalita(d); 323 var cur = (d && d.descSpaziale) || {}; 324 var payload = { 325 modalita: modalita, 326 suggestedBy: cur.suggestedBy || sug.suggestedBy, 327 decidedAt: new Date().toISOString(), 328 skipSez131: !!skipSez131 329 }; 330 if (typeof root.setDR === 'function') { 331 root.setDR('descSpaziale', payload); 332 } else if (typeof root.setD === 'function') { 333 root.setD('descSpaziale', payload); 334 if (typeof root.renderTab === 'function') root.renderTab(); 335 } 336 if (typeof onChange === 'function') onChange(payload); 337 return payload; 338 } 339 340 function _onModalitaChange(sel) { 341 var skipEl = root.document && root.document.getElementById('tec-mds-skip131'); 342 var m = sel && sel.value ? sel.value : getModalita(typeof root.D === 'function' ? root.D() : {}); 343 applyModalita(m, skipEl ? skipEl.checked : false); 344 } 345 346 function _onSkip131Change(cb) { 347 var sel = root.document && root.document.getElementById('tec-mds-modalita'); 348 var m = sel && sel.value ? sel.value : getModalita(typeof root.D === 'function' ? root.D() : {}); 349 applyModalita(m, cb ? cb.checked : false); 350 } 351 352 function selfTest() { 353 var errs = []; 354 var wlake = { 355 tipologiaAzienda: 'sport_wakeboard', 356 atecoPrimario: '93.11.00', 357 ambientiSede: [{ tipo: 'specchio_acqua', caratteristiche: ['acquatico', 'esterno'] }] 358 }; 359 if (suggestModalita(wlake).modalita !== MODALITA.NARRATIVA) errs.push('wlakeânarrativa'); 360 var modica = { atecoPrimario: '55.10.00', nPianiFT: '3' }; 361 if (suggestModalita(modica).modalita !== MODALITA.MULTIPIANO_GUIDATA) errs.push('modicaâmultipiano'); 362 var nar = { descSpaziale: { modalita: MODALITA.NARRATIVA, skipSez131: true } }; 363 if (shouldShowSez131(nar)) errs.push('skipSez131'); 364 if (getSez3DLabel({ descSpaziale: { modalita: MODALITA.MONOPIANO_GUIDATA } }) !== 'Zone e ambienti') errs.push('label monopiano'); 365 if (getSez3DLabel({ descSpaziale: { modalita: MODALITA.NARRATIVA } }) !== null) errs.push('label narrativa hidden'); 366 return { ok: !errs.length, errors: errs, VERSION: VERSION }; 367 } 368 369 root._tecDescSpaziale = { 370 VERSION: VERSION, 371 MODALITA: MODALITA, 372 MODALITA_LABELS: MODALITA_LABELS, 373 suggestModalita: suggestModalita, 374 getModalita: getModalita, 375 shouldShowSez131: shouldShowSez131, 376 shouldShowTab3D: shouldShowTab3D, 377 shouldShowNarrativaNote: shouldShowNarrativaNote, 378 getSez3DLabel: getSez3DLabel, 379 renderConfiguratorCard: renderConfiguratorCard, 380 renderPiani3DSection: renderPiani3DSection, 381 applyModalita: applyModalita, 382 _onModalitaChange: _onModalitaChange, 383 _onSkip131Change: _onSkip131Change, 384 selfTest: selfTest 385 }; 386})(typeof window !== 'undefined' ? window : globalThis);
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.