@memberjunction/ng-dashboard-viewer 6.2.0-edge.4 → 6.2.0-edge.5
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.
|
@@ -38,7 +38,13 @@ export declare class WebURLPartComponent extends BaseDashboardPart implements Af
|
|
|
38
38
|
private iframeLoaded;
|
|
39
39
|
constructor(cdr: ChangeDetectorRef, sanitizer: DomSanitizer);
|
|
40
40
|
ngAfterViewInit(): void;
|
|
41
|
+
protected cleanup(): void;
|
|
41
42
|
loadContent(): Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* True when the URL parses and uses a scheme that is safe to hand to
|
|
45
|
+
* bypassSecurityTrustResourceUrl for iframe embedding (http/https only).
|
|
46
|
+
*/
|
|
47
|
+
private isEmbeddableUrl;
|
|
42
48
|
/**
|
|
43
49
|
* Check if the iframe content might be blocked.
|
|
44
50
|
* Called after a timeout - if the iframe load event hasn't fired, show the fallback link.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"weburl-part.component.d.ts","sourceRoot":"","sources":["../../../src/lib/parts/weburl-part.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,iBAAiB,EAAE,UAAU,EAAa,aAAa,EAAE,MAAM,eAAe,CAAC;AACnG,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAE1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;;AAG1D;;;GAGG;AACH,qBA2Na,mBAAoB,SAAQ,iBAAkB,YAAW,aAAa;IAC1D,SAAS,EAAG,UAAU,CAAC,iBAAiB,CAAC,CAAC;IAE/D,yCAAyC;IACzC,IAAI,SAAS,IAAI,UAAU,CAAC,iBAAiB,CAAC,CAE7C;IACD,yCAAyC;IACzC,IAAI,SAAS,CAAC,KAAK,EAAE,UAAU,CAAC,iBAAiB,CAAC,EAEjD;IAEM,OAAO,EAAE,eAAe,GAAG,IAAI,CAAQ;IACvC,MAAM,EAAE,MAAM,CAAM;IAE3B,sCAAsC;IACtC,IAAW,MAAM,IAAI,MAAM,CAE1B;IACD,sCAAsC;IACtC,IAAW,MAAM,CAAC,KAAK,EAAE,MAAM,EAE9B;IACM,WAAW,EAAE,UAAU,GAAG,QAAQ,GAAG,YAAY,CAAc;IAEtE,2CAA2C;IAC3C,IAAW,WAAW,IAAI,UAAU,GAAG,QAAQ,GAAG,YAAY,CAE7D;IACD,2CAA2C;IAC3C,IAAW,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,QAAQ,GAAG,YAAY,EAEjE;IACM,eAAe,EAAE,OAAO,CAAQ;IAEvC,+CAA+C;IAC/C,IAAW,eAAe,IAAI,OAAO,CAEpC;IACD,+CAA+C;IAC/C,IAAW,eAAe,CAAC,KAAK,EAAE,OAAO,EAExC;IACM,gBAAgB,EAAE,OAAO,CAAS;IAEzC,gDAAgD;IAChD,IAAW,gBAAgB,IAAI,OAAO,CAErC;IACD,gDAAgD;IAChD,IAAW,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAEzC;IAED,OAAO,CAAC,SAAS,CAAe;IAChC,OAAO,CAAC,gBAAgB,CAA8C;IACtE,OAAO,CAAC,YAAY,CAAkB;gBAE1B,GAAG,EAAE,iBAAiB,EAAE,SAAS,EAAE,YAAY;IAK3D,eAAe,IAAI,IAAI;
|
|
1
|
+
{"version":3,"file":"weburl-part.component.d.ts","sourceRoot":"","sources":["../../../src/lib/parts/weburl-part.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,iBAAiB,EAAE,UAAU,EAAa,aAAa,EAAE,MAAM,eAAe,CAAC;AACnG,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAE1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;;AAG1D;;;GAGG;AACH,qBA2Na,mBAAoB,SAAQ,iBAAkB,YAAW,aAAa;IAC1D,SAAS,EAAG,UAAU,CAAC,iBAAiB,CAAC,CAAC;IAE/D,yCAAyC;IACzC,IAAI,SAAS,IAAI,UAAU,CAAC,iBAAiB,CAAC,CAE7C;IACD,yCAAyC;IACzC,IAAI,SAAS,CAAC,KAAK,EAAE,UAAU,CAAC,iBAAiB,CAAC,EAEjD;IAEM,OAAO,EAAE,eAAe,GAAG,IAAI,CAAQ;IACvC,MAAM,EAAE,MAAM,CAAM;IAE3B,sCAAsC;IACtC,IAAW,MAAM,IAAI,MAAM,CAE1B;IACD,sCAAsC;IACtC,IAAW,MAAM,CAAC,KAAK,EAAE,MAAM,EAE9B;IACM,WAAW,EAAE,UAAU,GAAG,QAAQ,GAAG,YAAY,CAAc;IAEtE,2CAA2C;IAC3C,IAAW,WAAW,IAAI,UAAU,GAAG,QAAQ,GAAG,YAAY,CAE7D;IACD,2CAA2C;IAC3C,IAAW,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,QAAQ,GAAG,YAAY,EAEjE;IACM,eAAe,EAAE,OAAO,CAAQ;IAEvC,+CAA+C;IAC/C,IAAW,eAAe,IAAI,OAAO,CAEpC;IACD,+CAA+C;IAC/C,IAAW,eAAe,CAAC,KAAK,EAAE,OAAO,EAExC;IACM,gBAAgB,EAAE,OAAO,CAAS;IAEzC,gDAAgD;IAChD,IAAW,gBAAgB,IAAI,OAAO,CAErC;IACD,gDAAgD;IAChD,IAAW,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAEzC;IAED,OAAO,CAAC,SAAS,CAAe;IAChC,OAAO,CAAC,gBAAgB,CAA8C;IACtE,OAAO,CAAC,YAAY,CAAkB;gBAE1B,GAAG,EAAE,iBAAiB,EAAE,SAAS,EAAE,YAAY;IAK3D,eAAe,IAAI,IAAI;cAOJ,OAAO,IAAI,IAAI;IAQrB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IA6DzC;;;OAGG;IACH,OAAO,CAAC,eAAe;IASvB;;;OAGG;IACH,OAAO,CAAC,cAAc;IAStB;;OAEG;IACI,YAAY,IAAI,IAAI;IAgB3B,4CAA4C;IACrC,YAAY,IAAI,IAAI;IAI3B;;OAEG;IACI,aAAa,CAAC,MAAM,EAAE,KAAK,GAAG,IAAI;IAKzC,6CAA6C;IACtC,aAAa,CAAC,MAAM,EAAE,KAAK,GAAG,IAAI;yCAtMhC,mBAAmB;2CAAnB,mBAAmB;CAyM/B"}
|
|
@@ -192,6 +192,13 @@ let WebURLPartComponent = class WebURLPartComponent extends BaseDashboardPart {
|
|
|
192
192
|
this.loadContent();
|
|
193
193
|
}
|
|
194
194
|
}
|
|
195
|
+
cleanup() {
|
|
196
|
+
if (this.loadCheckTimeout) {
|
|
197
|
+
clearTimeout(this.loadCheckTimeout);
|
|
198
|
+
this.loadCheckTimeout = null;
|
|
199
|
+
}
|
|
200
|
+
super.cleanup();
|
|
201
|
+
}
|
|
195
202
|
async loadContent() {
|
|
196
203
|
const config = this.getConfig();
|
|
197
204
|
const url = config?.['url'];
|
|
@@ -203,12 +210,22 @@ let WebURLPartComponent = class WebURLPartComponent extends BaseDashboardPart {
|
|
|
203
210
|
// Reset state
|
|
204
211
|
this.iframeLoaded = false;
|
|
205
212
|
this.ShowFallbackLink = false;
|
|
213
|
+
this.ErrorMessage = null;
|
|
206
214
|
if (!url) {
|
|
207
215
|
this.SafeUrl = null;
|
|
208
216
|
this.RawUrl = '';
|
|
209
217
|
this.cdr.detectChanges();
|
|
210
218
|
return;
|
|
211
219
|
}
|
|
220
|
+
// SECURITY: only http(s) URLs may be embedded. bypassSecurityTrustResourceUrl disables
|
|
221
|
+
// Angular's sanitizer, so without this check a stored javascript: (or other scheme) URL
|
|
222
|
+
// in a shared dashboard panel would execute in every viewer's browser.
|
|
223
|
+
if (!this.isEmbeddableUrl(url)) {
|
|
224
|
+
this.SafeUrl = null;
|
|
225
|
+
this.RawUrl = '';
|
|
226
|
+
this.setError('Only http:// and https:// URLs can be embedded.');
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
212
229
|
this.setLoading(true);
|
|
213
230
|
try {
|
|
214
231
|
// Store raw URL for fallback link
|
|
@@ -231,6 +248,19 @@ let WebURLPartComponent = class WebURLPartComponent extends BaseDashboardPart {
|
|
|
231
248
|
this.setError(error instanceof Error ? error.message : 'Failed to load URL');
|
|
232
249
|
}
|
|
233
250
|
}
|
|
251
|
+
/**
|
|
252
|
+
* True when the URL parses and uses a scheme that is safe to hand to
|
|
253
|
+
* bypassSecurityTrustResourceUrl for iframe embedding (http/https only).
|
|
254
|
+
*/
|
|
255
|
+
isEmbeddableUrl(url) {
|
|
256
|
+
try {
|
|
257
|
+
const parsed = new URL(url);
|
|
258
|
+
return parsed.protocol === 'http:' || parsed.protocol === 'https:';
|
|
259
|
+
}
|
|
260
|
+
catch {
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
234
264
|
/**
|
|
235
265
|
* Check if the iframe content might be blocked.
|
|
236
266
|
* Called after a timeout - if the iframe load event hasn't fired, show the fallback link.
|
|
@@ -280,7 +310,7 @@ let WebURLPartComponent = class WebURLPartComponent extends BaseDashboardPart {
|
|
|
280
310
|
} if (rf & 2) {
|
|
281
311
|
let _t;
|
|
282
312
|
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.IframeRef = _t.first);
|
|
283
|
-
} }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 6, vars: 9, consts: [["iframe", ""], [1, "weburl-part"], [1, "loading-state"], ["Variant", "error", "Icon", "fa-solid fa-triangle-exclamation", "Title", "Couldn't load content", "Size", "compact", 1, "part-placeholder", 3, "Message"], ["Icon", "fa-solid fa-globe", "Title", "No URL Configured", "Message", "Use Edit part to set a URL.", "Size", "compact", 1, "part-placeholder"], [1, "iframe-fallback"], [1, "fa-solid", "fa-spinner", "fa-spin"], ["actions", "", "target", "_blank", 1, "open-link", 3, "href"], [1, "fa-solid", "fa-up-right-from-square"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups", "allowfullscreen", "", 1, "content-iframe", 3, "src", "title"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups", 1, "content-iframe", 3, "src", "title"], ["sandbox", "allow-scripts", "allowfullscreen", "", 1, "content-iframe", 3, "src", "title"], ["sandbox", "allow-scripts", 1, "content-iframe", 3, "src", "title"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-top-navigation", "allowfullscreen", "", 1, "content-iframe", 3, "src", "title"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-top-navigation", 1, "content-iframe", 3, "src", "title"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups", "allowfullscreen", "", 1, "content-iframe", 3, "load", "error", "src", "title"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups", 1, "content-iframe", 3, "load", "error", "src", "title"], ["sandbox", "allow-scripts", "allowfullscreen", "", 1, "content-iframe", 3, "load", "error", "src", "title"], ["sandbox", "allow-scripts", 1, "content-iframe", 3, "load", "error", "src", "title"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-top-navigation", "allowfullscreen", "", 1, "content-iframe", 3, "load", "error", "src", "title"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-top-navigation", 1, "content-iframe", 3, "load", "error", "src", "title"], ["target", "_blank", 3, "href"], [1, "fa-solid", "fa-external-link-alt"]], template: function WebURLPartComponent_Template(rf, ctx) { if (rf & 1) {
|
|
313
|
+
} }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 6, vars: 9, consts: [["iframe", ""], [1, "weburl-part"], [1, "loading-state"], ["Variant", "error", "Icon", "fa-solid fa-triangle-exclamation", "Title", "Couldn't load content", "Size", "compact", 1, "part-placeholder", 3, "Message"], ["Icon", "fa-solid fa-globe", "Title", "No URL Configured", "Message", "Use Edit part to set a URL.", "Size", "compact", 1, "part-placeholder"], [1, "iframe-fallback"], [1, "fa-solid", "fa-spinner", "fa-spin"], ["actions", "", "target", "_blank", "rel", "noopener noreferrer", 1, "open-link", 3, "href"], [1, "fa-solid", "fa-up-right-from-square"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups", "allowfullscreen", "", 1, "content-iframe", 3, "src", "title"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups", 1, "content-iframe", 3, "src", "title"], ["sandbox", "allow-scripts", "allowfullscreen", "", 1, "content-iframe", 3, "src", "title"], ["sandbox", "allow-scripts", 1, "content-iframe", 3, "src", "title"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-top-navigation", "allowfullscreen", "", 1, "content-iframe", 3, "src", "title"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-top-navigation", 1, "content-iframe", 3, "src", "title"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups", "allowfullscreen", "", 1, "content-iframe", 3, "load", "error", "src", "title"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups", 1, "content-iframe", 3, "load", "error", "src", "title"], ["sandbox", "allow-scripts", "allowfullscreen", "", 1, "content-iframe", 3, "load", "error", "src", "title"], ["sandbox", "allow-scripts", 1, "content-iframe", 3, "load", "error", "src", "title"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-top-navigation", "allowfullscreen", "", 1, "content-iframe", 3, "load", "error", "src", "title"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-top-navigation", 1, "content-iframe", 3, "load", "error", "src", "title"], ["target", "_blank", "rel", "noopener noreferrer", 3, "href"], [1, "fa-solid", "fa-external-link-alt"]], template: function WebURLPartComponent_Template(rf, ctx) { if (rf & 1) {
|
|
284
314
|
i0.ɵɵelementStart(0, "div", 1);
|
|
285
315
|
i0.ɵɵconditionalCreate(1, WebURLPartComponent_Conditional_1_Template, 4, 0, "div", 2);
|
|
286
316
|
i0.ɵɵconditionalCreate(2, WebURLPartComponent_Conditional_2_Template, 2, 2, "mj-empty-state", 3);
|
|
@@ -328,7 +358,7 @@ export { WebURLPartComponent };
|
|
|
328
358
|
[Message]="ErrorMessage"
|
|
329
359
|
Size="compact">
|
|
330
360
|
@if (rawUrl) {
|
|
331
|
-
<a actions [href]="rawUrl" target="_blank" class="open-link">
|
|
361
|
+
<a actions [href]="rawUrl" target="_blank" rel="noopener noreferrer" class="open-link">
|
|
332
362
|
<i class="fa-solid fa-up-right-from-square"></i>
|
|
333
363
|
Open in new window
|
|
334
364
|
</a>
|
|
@@ -429,7 +459,7 @@ export { WebURLPartComponent };
|
|
|
429
459
|
@if (!IsLoading && !ErrorMessage && SafeUrl && showFallbackLink) {
|
|
430
460
|
<div class="iframe-fallback">
|
|
431
461
|
<span>If the content doesn't load:</span>
|
|
432
|
-
<a [href]="rawUrl" target="_blank">
|
|
462
|
+
<a [href]="rawUrl" target="_blank" rel="noopener noreferrer">
|
|
433
463
|
<i class="fa-solid fa-external-link-alt"></i>
|
|
434
464
|
Open in new window
|
|
435
465
|
</a>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"weburl-part.component.js","sourceRoot":"","sources":["../../../src/lib/parts/weburl-part.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, ChangeDetectorRef, ElementRef, ViewChild, AfterViewInit } from '@angular/core';\nimport { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';\nimport { RegisterClass } from '@memberjunction/global';\nimport { BaseDashboardPart } from './base-dashboard-part';\nimport { PanelConfig } from '../models/dashboard-types';\n\n/**\n * Runtime renderer for WebURL dashboard parts.\n * Displays web content in an iframe with configurable sandbox permissions.\n */\n@RegisterClass(BaseDashboardPart, 'WebURLPanelRenderer')\n@Component({\n standalone: false,\n selector: 'mj-weburl-part',\n template: `\n <div class=\"weburl-part\" [class.loading]=\"IsLoading\" [class.error]=\"ErrorMessage\">\n <!-- Loading state -->\n @if (IsLoading) {\n <div class=\"loading-state\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Loading...</span>\n </div>\n }\n \n <!-- Error state -->\n @if (ErrorMessage) {\n <mj-empty-state\n class=\"part-placeholder\"\n Variant=\"error\"\n Icon=\"fa-solid fa-triangle-exclamation\"\n Title=\"Couldn't load content\"\n [Message]=\"ErrorMessage\"\n Size=\"compact\">\n @if (rawUrl) {\n <a actions [href]=\"rawUrl\" target=\"_blank\" class=\"open-link\">\n <i class=\"fa-solid fa-up-right-from-square\"></i>\n Open in new window\n </a>\n }\n </mj-empty-state>\n }\n\n <!-- No URL configured -->\n @if (!IsLoading && !ErrorMessage && !SafeUrl) {\n <mj-empty-state\n class=\"part-placeholder\"\n Icon=\"fa-solid fa-globe\"\n Title=\"No URL Configured\"\n Message=\"Use Edit part to set a URL.\"\n Size=\"compact\" />\n }\n \n <!-- Iframe container - sandbox and allowfullscreen must be static, so we use ng-container to switch -->\n @if (!IsLoading && !ErrorMessage && SafeUrl) {\n <!-- Standard sandbox + fullscreen enabled -->\n @if (sandboxMode === 'standard' && allowFullscreen) {\n <iframe\n #iframe\n [src]=\"SafeUrl\"\n sandbox=\"allow-scripts allow-same-origin allow-forms allow-popups\"\n allowfullscreen\n [title]=\"Panel?.title || 'Embedded content'\"\n (load)=\"onIframeLoad()\"\n (error)=\"onIframeError($event)\"\n class=\"content-iframe\">\n </iframe>\n }\n <!-- Standard sandbox + fullscreen disabled -->\n @if (sandboxMode === 'standard' && !allowFullscreen) {\n <iframe\n #iframe\n [src]=\"SafeUrl\"\n sandbox=\"allow-scripts allow-same-origin allow-forms allow-popups\"\n [title]=\"Panel?.title || 'Embedded content'\"\n (load)=\"onIframeLoad()\"\n (error)=\"onIframeError($event)\"\n class=\"content-iframe\">\n </iframe>\n }\n <!-- Strict sandbox + fullscreen enabled -->\n @if (sandboxMode === 'strict' && allowFullscreen) {\n <iframe\n #iframe\n [src]=\"SafeUrl\"\n sandbox=\"allow-scripts\"\n allowfullscreen\n [title]=\"Panel?.title || 'Embedded content'\"\n (load)=\"onIframeLoad()\"\n (error)=\"onIframeError($event)\"\n class=\"content-iframe\">\n </iframe>\n }\n <!-- Strict sandbox + fullscreen disabled -->\n @if (sandboxMode === 'strict' && !allowFullscreen) {\n <iframe\n #iframe\n [src]=\"SafeUrl\"\n sandbox=\"allow-scripts\"\n [title]=\"Panel?.title || 'Embedded content'\"\n (load)=\"onIframeLoad()\"\n (error)=\"onIframeError($event)\"\n class=\"content-iframe\">\n </iframe>\n }\n <!-- Permissive sandbox + fullscreen enabled -->\n @if (sandboxMode === 'permissive' && allowFullscreen) {\n <iframe\n #iframe\n [src]=\"SafeUrl\"\n sandbox=\"allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-top-navigation\"\n allowfullscreen\n [title]=\"Panel?.title || 'Embedded content'\"\n (load)=\"onIframeLoad()\"\n (error)=\"onIframeError($event)\"\n class=\"content-iframe\">\n </iframe>\n }\n <!-- Permissive sandbox + fullscreen disabled -->\n @if (sandboxMode === 'permissive' && !allowFullscreen) {\n <iframe\n #iframe\n [src]=\"SafeUrl\"\n sandbox=\"allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-top-navigation\"\n [title]=\"Panel?.title || 'Embedded content'\"\n (load)=\"onIframeLoad()\"\n (error)=\"onIframeError($event)\"\n class=\"content-iframe\">\n </iframe>\n }\n }\n \n <!-- Fallback link shown below iframe if content might be blocked -->\n @if (!IsLoading && !ErrorMessage && SafeUrl && showFallbackLink) {\n <div class=\"iframe-fallback\">\n <span>If the content doesn't load:</span>\n <a [href]=\"rawUrl\" target=\"_blank\">\n <i class=\"fa-solid fa-external-link-alt\"></i>\n Open in new window\n </a>\n </div>\n }\n </div>\n `,\n styles: [`\n :host {\n display: block;\n width: 100%;\n height: 100%;\n }\n\n .weburl-part {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\n position: relative; /* Required for overlay positioning */\n }\n\n .loading-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: var(--mj-text-secondary);\n text-align: center;\n padding: 24px;\n }\n\n .loading-state i {\n font-size: 48px;\n color: var(--mj-text-muted);\n margin-bottom: 16px;\n }\n\n .part-placeholder {\n height: 100%;\n }\n\n /* Projected \"open in new window\" link inside the error empty-state */\n .open-link {\n padding: 8px 16px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n text-decoration: none;\n border-radius: 4px;\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 14px;\n }\n\n .open-link:hover {\n background: var(--mj-brand-primary-hover);\n }\n\n .content-iframe {\n width: 100%;\n height: 100%;\n border: none;\n flex: 1;\n }\n\n .iframe-fallback {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 8px;\n background: var(--mj-bg-surface-card);\n border-top: 1px solid var(--mj-border-default);\n font-size: 12px;\n color: var(--mj-text-secondary);\n }\n\n .iframe-fallback a {\n color: var(--mj-brand-primary);\n text-decoration: none;\n display: flex;\n align-items: center;\n gap: 4px;\n }\n\n .iframe-fallback a:hover {\n text-decoration: underline;\n }\n `]\n})\nexport class WebURLPartComponent extends BaseDashboardPart implements AfterViewInit {\n @ViewChild('iframe') IframeRef!: ElementRef<HTMLIFrameElement>;\n\n /** @deprecated Use {@link IframeRef}. */\n get iframeRef(): ElementRef<HTMLIFrameElement> {\n return this.IframeRef;\n }\n /** @deprecated Use {@link IframeRef}. */\n set iframeRef(value: ElementRef<HTMLIFrameElement>) {\n this.IframeRef = value;\n }\n\n public SafeUrl: SafeResourceUrl | null = null;\n public RawUrl: string = '';\n\n /** @deprecated Use {@link RawUrl}. */\n public get rawUrl(): string {\n return this.RawUrl;\n }\n /** @deprecated Use {@link RawUrl}. */\n public set rawUrl(value: string) {\n this.RawUrl = value;\n }\n public SandboxMode: 'standard' | 'strict' | 'permissive' = 'standard';\n\n /** @deprecated Use {@link SandboxMode}. */\n public get sandboxMode(): 'standard' | 'strict' | 'permissive' {\n return this.SandboxMode;\n }\n /** @deprecated Use {@link SandboxMode}. */\n public set sandboxMode(value: 'standard' | 'strict' | 'permissive') {\n this.SandboxMode = value;\n }\n public AllowFullscreen: boolean = true;\n\n /** @deprecated Use {@link AllowFullscreen}. */\n public get allowFullscreen(): boolean {\n return this.AllowFullscreen;\n }\n /** @deprecated Use {@link AllowFullscreen}. */\n public set allowFullscreen(value: boolean) {\n this.AllowFullscreen = value;\n }\n public ShowFallbackLink: boolean = false;\n\n /** @deprecated Use {@link ShowFallbackLink}. */\n public get showFallbackLink(): boolean {\n return this.ShowFallbackLink;\n }\n /** @deprecated Use {@link ShowFallbackLink}. */\n public set showFallbackLink(value: boolean) {\n this.ShowFallbackLink = value;\n } // Hidden by default, shown if content might be blocked\n\n private sanitizer: DomSanitizer;\n private loadCheckTimeout: ReturnType<typeof setTimeout> | null = null;\n private iframeLoaded: boolean = false;\n\n constructor(cdr: ChangeDetectorRef, sanitizer: DomSanitizer) {\n super(cdr);\n this.sanitizer = sanitizer;\n }\n\n ngAfterViewInit(): void {\n // Initial load if panel is already set\n if (this.Panel) {\n this.loadContent();\n }\n }\n\n public async loadContent(): Promise<void> {\n const config = this.getConfig<PanelConfig>();\n const url = config?.['url'] as string | undefined;\n\n // Clear any existing timeout\n if (this.loadCheckTimeout) {\n clearTimeout(this.loadCheckTimeout);\n this.loadCheckTimeout = null;\n }\n\n // Reset state\n this.iframeLoaded = false;\n this.ShowFallbackLink = false;\n\n if (!url) {\n this.SafeUrl = null;\n this.RawUrl = '';\n this.cdr.detectChanges();\n return;\n }\n\n this.setLoading(true);\n\n try {\n // Store raw URL for fallback link\n this.RawUrl = url;\n\n // Sanitize and set URL for iframe src binding\n this.SafeUrl = this.sanitizer.bypassSecurityTrustResourceUrl(url);\n\n // Set sandbox mode (used by template to select correct iframe)\n this.SandboxMode = (config?.['sandboxMode'] as 'standard' | 'strict' | 'permissive') || 'standard';\n\n // Set fullscreen permission\n this.AllowFullscreen = (config?.['allowFullscreen'] as boolean) ?? true;\n\n this.setLoading(false);\n\n // Set up a delayed check to detect if embedding might be blocked\n // If after 3 seconds the iframe hasn't successfully loaded content we can access,\n // show the blocked overlay. This is a heuristic since we can't detect X-Frame-Options directly.\n this.loadCheckTimeout = setTimeout(() => {\n this.checkIfBlocked();\n }, 3000);\n\n } catch (error) {\n this.setError(error instanceof Error ? error.message : 'Failed to load URL');\n }\n }\n\n /**\n * Check if the iframe content might be blocked.\n * Called after a timeout - if the iframe load event hasn't fired, show the fallback link.\n */\n private checkIfBlocked(): void {\n // If the load event hasn't fired after the timeout, content might be blocked\n if (!this.iframeLoaded) {\n this.ShowFallbackLink = true;\n this.cdr.detectChanges();\n }\n // If iframeLoaded is true, content loaded successfully - keep fallback hidden\n }\n\n /**\n * Handle iframe load event\n */\n public OnIframeLoad(): void {\n // Mark that the load event fired\n this.iframeLoaded = true;\n\n // Clear the timeout since the iframe loaded\n if (this.loadCheckTimeout) {\n clearTimeout(this.loadCheckTimeout);\n this.loadCheckTimeout = null;\n }\n\n // The iframe loaded something - could still be blocked content\n // We can't reliably detect X-Frame-Options blocking from JS\n // The fallback link provides a way for users to open the site if it doesn't load\n this.cdr.detectChanges();\n }\n\n /** @deprecated Use {@link OnIframeLoad}. */\n public onIframeLoad(): void {\n return this.OnIframeLoad();\n }\n\n /**\n * Handle iframe error event\n */\n public OnIframeError(_event: Event): void {\n // This may fire for some load failures, but not for X-Frame-Options\n this.setError('This site cannot be embedded. It may block iframe embedding for security reasons.');\n }\n\n /** @deprecated Use {@link OnIframeError}. */\n public onIframeError(_event: Event): void {\n return this.OnIframeError(_event);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"weburl-part.component.js","sourceRoot":"","sources":["../../../src/lib/parts/weburl-part.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, ChangeDetectorRef, ElementRef, ViewChild, AfterViewInit } from '@angular/core';\nimport { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';\nimport { RegisterClass } from '@memberjunction/global';\nimport { BaseDashboardPart } from './base-dashboard-part';\nimport { PanelConfig } from '../models/dashboard-types';\n\n/**\n * Runtime renderer for WebURL dashboard parts.\n * Displays web content in an iframe with configurable sandbox permissions.\n */\n@RegisterClass(BaseDashboardPart, 'WebURLPanelRenderer')\n@Component({\n standalone: false,\n selector: 'mj-weburl-part',\n template: `\n <div class=\"weburl-part\" [class.loading]=\"IsLoading\" [class.error]=\"ErrorMessage\">\n <!-- Loading state -->\n @if (IsLoading) {\n <div class=\"loading-state\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Loading...</span>\n </div>\n }\n \n <!-- Error state -->\n @if (ErrorMessage) {\n <mj-empty-state\n class=\"part-placeholder\"\n Variant=\"error\"\n Icon=\"fa-solid fa-triangle-exclamation\"\n Title=\"Couldn't load content\"\n [Message]=\"ErrorMessage\"\n Size=\"compact\">\n @if (rawUrl) {\n <a actions [href]=\"rawUrl\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"open-link\">\n <i class=\"fa-solid fa-up-right-from-square\"></i>\n Open in new window\n </a>\n }\n </mj-empty-state>\n }\n\n <!-- No URL configured -->\n @if (!IsLoading && !ErrorMessage && !SafeUrl) {\n <mj-empty-state\n class=\"part-placeholder\"\n Icon=\"fa-solid fa-globe\"\n Title=\"No URL Configured\"\n Message=\"Use Edit part to set a URL.\"\n Size=\"compact\" />\n }\n \n <!-- Iframe container - sandbox and allowfullscreen must be static, so we use ng-container to switch -->\n @if (!IsLoading && !ErrorMessage && SafeUrl) {\n <!-- Standard sandbox + fullscreen enabled -->\n @if (sandboxMode === 'standard' && allowFullscreen) {\n <iframe\n #iframe\n [src]=\"SafeUrl\"\n sandbox=\"allow-scripts allow-same-origin allow-forms allow-popups\"\n allowfullscreen\n [title]=\"Panel?.title || 'Embedded content'\"\n (load)=\"onIframeLoad()\"\n (error)=\"onIframeError($event)\"\n class=\"content-iframe\">\n </iframe>\n }\n <!-- Standard sandbox + fullscreen disabled -->\n @if (sandboxMode === 'standard' && !allowFullscreen) {\n <iframe\n #iframe\n [src]=\"SafeUrl\"\n sandbox=\"allow-scripts allow-same-origin allow-forms allow-popups\"\n [title]=\"Panel?.title || 'Embedded content'\"\n (load)=\"onIframeLoad()\"\n (error)=\"onIframeError($event)\"\n class=\"content-iframe\">\n </iframe>\n }\n <!-- Strict sandbox + fullscreen enabled -->\n @if (sandboxMode === 'strict' && allowFullscreen) {\n <iframe\n #iframe\n [src]=\"SafeUrl\"\n sandbox=\"allow-scripts\"\n allowfullscreen\n [title]=\"Panel?.title || 'Embedded content'\"\n (load)=\"onIframeLoad()\"\n (error)=\"onIframeError($event)\"\n class=\"content-iframe\">\n </iframe>\n }\n <!-- Strict sandbox + fullscreen disabled -->\n @if (sandboxMode === 'strict' && !allowFullscreen) {\n <iframe\n #iframe\n [src]=\"SafeUrl\"\n sandbox=\"allow-scripts\"\n [title]=\"Panel?.title || 'Embedded content'\"\n (load)=\"onIframeLoad()\"\n (error)=\"onIframeError($event)\"\n class=\"content-iframe\">\n </iframe>\n }\n <!-- Permissive sandbox + fullscreen enabled -->\n @if (sandboxMode === 'permissive' && allowFullscreen) {\n <iframe\n #iframe\n [src]=\"SafeUrl\"\n sandbox=\"allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-top-navigation\"\n allowfullscreen\n [title]=\"Panel?.title || 'Embedded content'\"\n (load)=\"onIframeLoad()\"\n (error)=\"onIframeError($event)\"\n class=\"content-iframe\">\n </iframe>\n }\n <!-- Permissive sandbox + fullscreen disabled -->\n @if (sandboxMode === 'permissive' && !allowFullscreen) {\n <iframe\n #iframe\n [src]=\"SafeUrl\"\n sandbox=\"allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-top-navigation\"\n [title]=\"Panel?.title || 'Embedded content'\"\n (load)=\"onIframeLoad()\"\n (error)=\"onIframeError($event)\"\n class=\"content-iframe\">\n </iframe>\n }\n }\n \n <!-- Fallback link shown below iframe if content might be blocked -->\n @if (!IsLoading && !ErrorMessage && SafeUrl && showFallbackLink) {\n <div class=\"iframe-fallback\">\n <span>If the content doesn't load:</span>\n <a [href]=\"rawUrl\" target=\"_blank\" rel=\"noopener noreferrer\">\n <i class=\"fa-solid fa-external-link-alt\"></i>\n Open in new window\n </a>\n </div>\n }\n </div>\n `,\n styles: [`\n :host {\n display: block;\n width: 100%;\n height: 100%;\n }\n\n .weburl-part {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\n position: relative; /* Required for overlay positioning */\n }\n\n .loading-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: var(--mj-text-secondary);\n text-align: center;\n padding: 24px;\n }\n\n .loading-state i {\n font-size: 48px;\n color: var(--mj-text-muted);\n margin-bottom: 16px;\n }\n\n .part-placeholder {\n height: 100%;\n }\n\n /* Projected \"open in new window\" link inside the error empty-state */\n .open-link {\n padding: 8px 16px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n text-decoration: none;\n border-radius: 4px;\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 14px;\n }\n\n .open-link:hover {\n background: var(--mj-brand-primary-hover);\n }\n\n .content-iframe {\n width: 100%;\n height: 100%;\n border: none;\n flex: 1;\n }\n\n .iframe-fallback {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 8px;\n background: var(--mj-bg-surface-card);\n border-top: 1px solid var(--mj-border-default);\n font-size: 12px;\n color: var(--mj-text-secondary);\n }\n\n .iframe-fallback a {\n color: var(--mj-brand-primary);\n text-decoration: none;\n display: flex;\n align-items: center;\n gap: 4px;\n }\n\n .iframe-fallback a:hover {\n text-decoration: underline;\n }\n `]\n})\nexport class WebURLPartComponent extends BaseDashboardPart implements AfterViewInit {\n @ViewChild('iframe') IframeRef!: ElementRef<HTMLIFrameElement>;\n\n /** @deprecated Use {@link IframeRef}. */\n get iframeRef(): ElementRef<HTMLIFrameElement> {\n return this.IframeRef;\n }\n /** @deprecated Use {@link IframeRef}. */\n set iframeRef(value: ElementRef<HTMLIFrameElement>) {\n this.IframeRef = value;\n }\n\n public SafeUrl: SafeResourceUrl | null = null;\n public RawUrl: string = '';\n\n /** @deprecated Use {@link RawUrl}. */\n public get rawUrl(): string {\n return this.RawUrl;\n }\n /** @deprecated Use {@link RawUrl}. */\n public set rawUrl(value: string) {\n this.RawUrl = value;\n }\n public SandboxMode: 'standard' | 'strict' | 'permissive' = 'standard';\n\n /** @deprecated Use {@link SandboxMode}. */\n public get sandboxMode(): 'standard' | 'strict' | 'permissive' {\n return this.SandboxMode;\n }\n /** @deprecated Use {@link SandboxMode}. */\n public set sandboxMode(value: 'standard' | 'strict' | 'permissive') {\n this.SandboxMode = value;\n }\n public AllowFullscreen: boolean = true;\n\n /** @deprecated Use {@link AllowFullscreen}. */\n public get allowFullscreen(): boolean {\n return this.AllowFullscreen;\n }\n /** @deprecated Use {@link AllowFullscreen}. */\n public set allowFullscreen(value: boolean) {\n this.AllowFullscreen = value;\n }\n public ShowFallbackLink: boolean = false;\n\n /** @deprecated Use {@link ShowFallbackLink}. */\n public get showFallbackLink(): boolean {\n return this.ShowFallbackLink;\n }\n /** @deprecated Use {@link ShowFallbackLink}. */\n public set showFallbackLink(value: boolean) {\n this.ShowFallbackLink = value;\n } // Hidden by default, shown if content might be blocked\n\n private sanitizer: DomSanitizer;\n private loadCheckTimeout: ReturnType<typeof setTimeout> | null = null;\n private iframeLoaded: boolean = false;\n\n constructor(cdr: ChangeDetectorRef, sanitizer: DomSanitizer) {\n super(cdr);\n this.sanitizer = sanitizer;\n }\n\n ngAfterViewInit(): void {\n // Initial load if panel is already set\n if (this.Panel) {\n this.loadContent();\n }\n }\n\n protected override cleanup(): void {\n if (this.loadCheckTimeout) {\n clearTimeout(this.loadCheckTimeout);\n this.loadCheckTimeout = null;\n }\n super.cleanup();\n }\n\n public async loadContent(): Promise<void> {\n const config = this.getConfig<PanelConfig>();\n const url = config?.['url'] as string | undefined;\n\n // Clear any existing timeout\n if (this.loadCheckTimeout) {\n clearTimeout(this.loadCheckTimeout);\n this.loadCheckTimeout = null;\n }\n\n // Reset state\n this.iframeLoaded = false;\n this.ShowFallbackLink = false;\n this.ErrorMessage = null;\n\n if (!url) {\n this.SafeUrl = null;\n this.RawUrl = '';\n this.cdr.detectChanges();\n return;\n }\n\n // SECURITY: only http(s) URLs may be embedded. bypassSecurityTrustResourceUrl disables\n // Angular's sanitizer, so without this check a stored javascript: (or other scheme) URL\n // in a shared dashboard panel would execute in every viewer's browser.\n if (!this.isEmbeddableUrl(url)) {\n this.SafeUrl = null;\n this.RawUrl = '';\n this.setError('Only http:// and https:// URLs can be embedded.');\n return;\n }\n\n this.setLoading(true);\n\n try {\n // Store raw URL for fallback link\n this.RawUrl = url;\n\n // Sanitize and set URL for iframe src binding\n this.SafeUrl = this.sanitizer.bypassSecurityTrustResourceUrl(url);\n\n // Set sandbox mode (used by template to select correct iframe)\n this.SandboxMode = (config?.['sandboxMode'] as 'standard' | 'strict' | 'permissive') || 'standard';\n\n // Set fullscreen permission\n this.AllowFullscreen = (config?.['allowFullscreen'] as boolean) ?? true;\n\n this.setLoading(false);\n\n // Set up a delayed check to detect if embedding might be blocked\n // If after 3 seconds the iframe hasn't successfully loaded content we can access,\n // show the blocked overlay. This is a heuristic since we can't detect X-Frame-Options directly.\n this.loadCheckTimeout = setTimeout(() => {\n this.checkIfBlocked();\n }, 3000);\n\n } catch (error) {\n this.setError(error instanceof Error ? error.message : 'Failed to load URL');\n }\n }\n\n /**\n * True when the URL parses and uses a scheme that is safe to hand to\n * bypassSecurityTrustResourceUrl for iframe embedding (http/https only).\n */\n private isEmbeddableUrl(url: string): boolean {\n try {\n const parsed = new URL(url);\n return parsed.protocol === 'http:' || parsed.protocol === 'https:';\n } catch {\n return false;\n }\n }\n\n /**\n * Check if the iframe content might be blocked.\n * Called after a timeout - if the iframe load event hasn't fired, show the fallback link.\n */\n private checkIfBlocked(): void {\n // If the load event hasn't fired after the timeout, content might be blocked\n if (!this.iframeLoaded) {\n this.ShowFallbackLink = true;\n this.cdr.detectChanges();\n }\n // If iframeLoaded is true, content loaded successfully - keep fallback hidden\n }\n\n /**\n * Handle iframe load event\n */\n public OnIframeLoad(): void {\n // Mark that the load event fired\n this.iframeLoaded = true;\n\n // Clear the timeout since the iframe loaded\n if (this.loadCheckTimeout) {\n clearTimeout(this.loadCheckTimeout);\n this.loadCheckTimeout = null;\n }\n\n // The iframe loaded something - could still be blocked content\n // We can't reliably detect X-Frame-Options blocking from JS\n // The fallback link provides a way for users to open the site if it doesn't load\n this.cdr.detectChanges();\n }\n\n /** @deprecated Use {@link OnIframeLoad}. */\n public onIframeLoad(): void {\n return this.OnIframeLoad();\n }\n\n /**\n * Handle iframe error event\n */\n public OnIframeError(_event: Event): void {\n // This may fire for some load failures, but not for X-Frame-Options\n this.setError('This site cannot be embedded. It may block iframe embedding for security reasons.');\n }\n\n /** @deprecated Use {@link OnIframeError}. */\n public onIframeError(_event: Event): void {\n return this.OnIframeError(_event);\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-dashboard-viewer",
|
|
3
|
-
"version": "6.2.0-edge.
|
|
3
|
+
"version": "6.2.0-edge.5",
|
|
4
4
|
"description": "MemberJunction: Angular components for metadata-driven dashboards with Golden Layout panels, supporting views, queries, artifacts, and custom content",
|
|
5
5
|
"main": "./dist/public-api.js",
|
|
6
6
|
"typings": "./dist/public-api.d.ts",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@angular/compiler": "21.2.22",
|
|
23
23
|
"@angular/compiler-cli": "21.2.22",
|
|
24
|
-
"@memberjunction/ng-test-utils": "6.2.0-edge.
|
|
24
|
+
"@memberjunction/ng-test-utils": "6.2.0-edge.5",
|
|
25
25
|
"vitest": "^4.0.18"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
@@ -33,17 +33,17 @@
|
|
|
33
33
|
"golden-layout": "^2.6.0"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@memberjunction/ng-ui-components": "6.2.0-edge.
|
|
37
|
-
"@memberjunction/core": "6.2.0-edge.
|
|
38
|
-
"@memberjunction/core-entities": "6.2.0-edge.
|
|
39
|
-
"@memberjunction/global": "6.2.0-edge.
|
|
40
|
-
"@memberjunction/ng-artifacts": "6.2.0-edge.
|
|
41
|
-
"@memberjunction/ng-base-types": "6.2.0-edge.
|
|
42
|
-
"@memberjunction/ng-entity-viewer": "6.2.0-edge.
|
|
43
|
-
"@memberjunction/ng-map-view": "6.2.0-edge.
|
|
44
|
-
"@memberjunction/ng-query-viewer": "6.2.0-edge.
|
|
45
|
-
"@memberjunction/ng-shared-generic": "6.2.0-edge.
|
|
46
|
-
"@memberjunction/ng-trees": "6.2.0-edge.
|
|
36
|
+
"@memberjunction/ng-ui-components": "6.2.0-edge.5",
|
|
37
|
+
"@memberjunction/core": "6.2.0-edge.5",
|
|
38
|
+
"@memberjunction/core-entities": "6.2.0-edge.5",
|
|
39
|
+
"@memberjunction/global": "6.2.0-edge.5",
|
|
40
|
+
"@memberjunction/ng-artifacts": "6.2.0-edge.5",
|
|
41
|
+
"@memberjunction/ng-base-types": "6.2.0-edge.5",
|
|
42
|
+
"@memberjunction/ng-entity-viewer": "6.2.0-edge.5",
|
|
43
|
+
"@memberjunction/ng-map-view": "6.2.0-edge.5",
|
|
44
|
+
"@memberjunction/ng-query-viewer": "6.2.0-edge.5",
|
|
45
|
+
"@memberjunction/ng-shared-generic": "6.2.0-edge.5",
|
|
46
|
+
"@memberjunction/ng-trees": "6.2.0-edge.5",
|
|
47
47
|
"rxjs": "^7.8.2",
|
|
48
48
|
"tslib": "^2.8.1"
|
|
49
49
|
},
|