|
122 | 122 | :ld-context="bblock.ldContext" |
123 | 123 | /> |
124 | 124 | </div> |
| 125 | + <sandboxed-iframe |
| 126 | + v-else-if="transformOutputView === 'web' && transformOutputIsHtml && language.transformEntry.url" |
| 127 | + :src="language.transformEntry.url" |
| 128 | + /> |
125 | 129 | <div v-else style="max-height: 30em; overflow-y: auto"> |
126 | 130 | <code-viewer |
127 | 131 | :code="transformOutputStatus.contents" |
|
136 | 140 | </div> |
137 | 141 | <div class="d-flex align-center mt-1" v-if="language.transformEntry.success && transformOutputStatus.contents"> |
138 | 142 | <v-btn-toggle |
139 | | - v-if="transformOutputGeoJson" |
| 143 | + v-if="transformOutputGeoJson || transformOutputIsHtml" |
140 | 144 | v-model="transformOutputView" |
141 | 145 | mandatory |
142 | 146 | density="compact" |
143 | 147 | rounded="1" |
144 | 148 | > |
145 | 149 | <v-btn value="code" size="small" prepend-icon="mdi-code-tags">Code</v-btn> |
146 | | - <v-btn value="map" size="small" prepend-icon="mdi-map">Map</v-btn> |
| 150 | + <v-btn v-if="transformOutputGeoJson" value="map" size="small" prepend-icon="mdi-map">Map</v-btn> |
| 151 | + <v-btn v-if="transformOutputIsHtml" value="web" size="small" prepend-icon="mdi-web">Web</v-btn> |
147 | 152 | </v-btn-toggle> |
148 | 153 | <v-spacer /> |
149 | 154 | <copy-to-clipboard-button :text="transformOutputStatus.contents" color="primary" variant="flat">Copy to clipboard</copy-to-clipboard-button> |
|
180 | 185 | <geo-json-map-viewer :geojson="geoJsonData" :ld-context="bblock.ldContext"></geo-json-map-viewer> |
181 | 186 | </div> |
182 | 187 | </template> |
| 188 | + <template v-else-if="isWebView && webViewUrl"> |
| 189 | + <sandboxed-iframe :src="webViewUrl" /> |
| 190 | + </template> |
183 | 191 | <template v-else-if="currentSnippet"> |
184 | 192 | <div style="max-height: 30em; overflow-y: auto"> |
185 | 193 | <code-viewer |
@@ -268,12 +276,13 @@ import JsonLdIcon from '@/assets/json-ld-data-white.svg'; |
268 | 276 | import MarkdownText from "@/components/MarkdownText.vue"; |
269 | 277 | import GeoJsonMapViewer from "@/components/bblock/GeoJsonMapViewer.vue"; |
270 | 278 | import TransformInfo from "@/components/bblock/TransformInfo.vue"; |
271 | | -import { geoJsonLanguageIds } from "@/models/mime-types"; |
| 279 | +import { geoJsonLanguageIds, htmlLanguageIds } from "@/models/mime-types"; |
272 | 280 | import { getTypeColor } from "@/models/transforms"; |
273 | 281 | import { useFetchDocumentByUrl } from "@/composables/bblock"; |
274 | 282 | import { useBBlockNavigation } from "@/composables/bblock-navigation"; |
275 | 283 | import CopyToClipboardButton from "@/components/CopyToClipboardButton.vue"; |
276 | 284 | import ProfilesValidationReportDialog from "@/components/bblock/ProfilesValidationReportDialog.vue"; |
| 285 | +import SandboxedIframe from "@/components/bblock/SandboxedIframe.vue"; |
277 | 286 | import bblockService from "@/services/bblock.service"; |
278 | 287 |
|
279 | 288 | const props = defineProps({ |
@@ -309,10 +318,18 @@ watch(() => props.language?.transformEntry?.profilesValidation, async (pv) => { |
309 | 318 | }, { immediate: true }); |
310 | 319 |
|
311 | 320 | const isMapView = computed(() => props.language?.id === 'map-view'); |
| 321 | +const isWebView = computed(() => props.language?.id === 'web-view'); |
312 | 322 | const isTransformView = computed(() => props.language?.isTransform === true); |
313 | 323 |
|
| 324 | +const webViewUrl = computed(() => { |
| 325 | + if (!isWebView.value) return null; |
| 326 | + return props.example?.snippets?.find(s => |
| 327 | + htmlLanguageIds.has(s.language?.id) && /^https?:\/\//.test(s.url) |
| 328 | + )?.url ?? null; |
| 329 | +}); |
| 330 | +
|
314 | 331 | const currentSnippet = computed(() => { |
315 | | - if (isTransformView.value) return null; |
| 332 | + if (isTransformView.value || isWebView.value) return null; |
316 | 333 | return (props.language && props.example?.snippets?.find(s => !s.language || s.language.id === props.language.id)) |
317 | 334 | || props.example?.snippets?.[0]; |
318 | 335 | }); |
@@ -362,6 +379,17 @@ const transformOutputStatus = reactive(useFetchDocumentByUrl( |
362 | 379 | computed(() => props.language?.transformEntry?.url ?? null) |
363 | 380 | )); |
364 | 381 |
|
| 382 | +const htmlMimeTypes = new Set(['text/html', 'application/xhtml+xml']); |
| 383 | +
|
| 384 | +const transformOutputIsHtml = computed(() => { |
| 385 | + const mediaTypes = props.language?.transform?.outputs?.mediaTypes; |
| 386 | + if (!mediaTypes?.length) return false; |
| 387 | + return mediaTypes.some(mt => { |
| 388 | + const mime = typeof mt === 'string' ? mt : mt?.mimeType; |
| 389 | + return mime && htmlMimeTypes.has(mime); |
| 390 | + }); |
| 391 | +}); |
| 392 | +
|
365 | 393 | const transformOutputGeoJson = computed(() => { |
366 | 394 | if (!transformOutputStatus.contents) return null; |
367 | 395 | try { |
|
0 commit comments