diff --git a/scripts/langindex.json b/scripts/langindex.json index ba9376e82..6279d4d95 100644 --- a/scripts/langindex.json +++ b/scripts/langindex.json @@ -1403,6 +1403,7 @@ "core.more": "moodle", "core.mygroups": "group", "core.name": "moodle", + "core.networkerroriframemsg": "local_moodlemobileapp", "core.networkerrormsg": "local_moodlemobileapp", "core.never": "moodle", "core.next": "moodle", diff --git a/src/app/app.scss b/src/app/app.scss index d465feaf8..0813e34e9 100644 --- a/src/app/app.scss +++ b/src/app/app.scss @@ -941,6 +941,10 @@ ion-app.app-root { &.disable-scroll ion-modal .ion-page { pointer-events: initial; } + + .core-iframe-offline-disabled { + display: none !important; + } } @each $color-name, $color-base, $color-contrast in get-colors($colors) { diff --git a/src/components/icon/icon.ts b/src/components/icon/icon.ts index 8c058ea50..931a06875 100644 --- a/src/components/icon/icon.ts +++ b/src/components/icon/icon.ts @@ -66,7 +66,20 @@ export class CoreIconComponent implements OnInit { const attrs = this.element.attributes; for (let i = attrs.length - 1; i >= 0; i--) { - newElement.setAttribute(attrs[i].name, attrs[i].value); + if (attrs[i].name == 'class') { + // We don't want to override the classes we already added. Add them one by one. + if (attrs[i].value) { + const classes = attrs[i].value.split(' '); + for (let j = 0; j < classes.length; j++) { + if (classes[j]) { + newElement.classList.add(classes[j]); + } + } + } + + } else { + newElement.setAttribute(attrs[i].name, attrs[i].value); + } } this.element.parentElement.replaceChild(newElement, this.element); @@ -79,12 +92,12 @@ export class CoreIconComponent implements OnInit { * @return {boolean} If has a value equivalent to true. */ isTrueProperty(val: any): boolean { - if (typeof val === 'string') { - val = val.toLowerCase().trim(); + if (typeof val === 'string') { + val = val.toLowerCase().trim(); - return (val === 'true' || val === 'on' || val === ''); - } + return (val === 'true' || val === 'on' || val === ''); + } - return !!val; + return !!val; } } diff --git a/src/lang/en.json b/src/lang/en.json index 4e165a8b6..843476b1f 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -153,6 +153,7 @@ "mygroups": "My groups", "name": "Name", "nograde": "No grade", + "networkerroriframemsg": "This content is not available offline. Please connect to the internet and try again.", "networkerrormsg": "There was a problem connecting to the site. Please check your connection and try again.", "never": "Never", "next": "Next", diff --git a/src/providers/utils/dom.ts b/src/providers/utils/dom.ts index 4ed2f7bee..979a01980 100644 --- a/src/providers/utils/dom.ts +++ b/src/providers/utils/dom.ts @@ -429,6 +429,18 @@ export class CoreDomUtilsProvider { return parseInt(style[measure], 10) || 0; } + /** + * Get the HTML code to render a connection warning icon. + * + * @return {string} HTML Code. + */ + getConnectionWarningIconHtml(): string { + return '
' + this.translate.instant('core.networkerroriframemsg') + '
'; + + element.parentElement.insertBefore(div, element); + + // Add a class to specify that the iframe is hidden. + element.classList.add('core-iframe-offline-disabled'); + + if (isSubframe) { + // We cannot apply CSS styles in subframes, just hide the iframe. + element.style.display = 'none'; + } + + // If the network changes, check it again. + const subscription = this.network.onConnect().subscribe(() => { + // Execute the callback in the Angular zone, so change detection doesn't stop working. + this.zone.run(() => { + if (!this.checkOnlineFrameInOffline(element, isSubframe)) { + // Now the app is online, no need to check connection again. + subscription.unsubscribe(); + } + }); + }); + + return true; + } else if (element.classList.contains('core-iframe-offline-disabled')) { + // Reload the frame. + element.src = element.src; + element.data = element.data; + + // Remove the warning and show the iframe + this.domUtils.removeElement(element.parentElement, 'div.core-iframe-offline-warning'); + element.classList.remove('core-iframe-offline-disabled'); + + if (isSubframe) { + element.style.display = ''; + } + } + + return false; + } + /** * Given an element, return the content window and document. * Please notice that the element should be an iframe, embed or similar. @@ -136,7 +204,7 @@ export class CoreIframeUtilsProvider { CoreIframeUtilsProvider.FRAME_TAGS.forEach((tag) => { const elements = Array.from(contentDocument.querySelectorAll(tag)); elements.forEach((subElement) => { - this.treatFrame(subElement); + this.treatFrame(subElement, true); }); }); } @@ -147,9 +215,12 @@ export class CoreIframeUtilsProvider { * Search links () and open them in browser or InAppBrowser if needed. * * @param {any} element Element to treat (iframe, embed, ...). + * @param {boolean} [isSubframe] Whether it's a frame inside another frame. */ - treatFrame(element: any): void { + treatFrame(element: any, isSubframe?: boolean): void { if (element) { + this.checkOnlineFrameInOffline(element, isSubframe); + let winAndDoc = this.getContentWindowAndDocument(element); // Redefine window.open in this element and sub frames, it might have been loaded already. this.redefineWindowOpen(element, winAndDoc.window, winAndDoc.document); @@ -157,6 +228,8 @@ export class CoreIframeUtilsProvider { this.treatFrameLinks(element, winAndDoc.document); element.addEventListener('load', () => { + this.checkOnlineFrameInOffline(element, isSubframe); + // Element loaded, redefine window.open and treat links again. winAndDoc = this.getContentWindowAndDocument(element); this.redefineWindowOpen(element, winAndDoc.window, winAndDoc.document);