diff --git a/src/directives/external-content.ts b/src/directives/external-content.ts index 93a20ee76..b68273ad8 100644 --- a/src/directives/external-content.ts +++ b/src/directives/external-content.ts @@ -20,6 +20,7 @@ import { CoreFilepoolProvider } from '@providers/filepool'; import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreUrlUtilsProvider } from '@providers/utils/url'; +import { CoreUtilsProvider } from '@providers/utils/utils'; /** * Directive to handle external content. @@ -28,6 +29,8 @@ import { CoreUrlUtilsProvider } from '@providers/utils/url'; * which we want to have available when the app is offline. Typically media and links. * * If a file is downloaded, its URL will be replaced by the local file URL. + * + * From v3.5.2 this directive will also download inline styles, so it can be used in any element as long as it has inline styles. */ @Directive({ selector: '[core-external-content]' @@ -42,7 +45,7 @@ export class CoreExternalContentDirective implements AfterViewInit { constructor(element: ElementRef, logger: CoreLoggerProvider, private filepoolProvider: CoreFilepoolProvider, private platform: Platform, private sitesProvider: CoreSitesProvider, private domUtils: CoreDomUtilsProvider, - private urlUtils: CoreUrlUtilsProvider, private appProvider: CoreAppProvider) { + private urlUtils: CoreUrlUtilsProvider, private appProvider: CoreAppProvider, private utils: CoreUtilsProvider) { // This directive can be added dynamically. In that case, the first param is the HTMLElement. this.element = element.nativeElement || element; this.logger = logger.getInstance('CoreExternalContentDirective'); @@ -58,6 +61,11 @@ export class CoreExternalContentDirective implements AfterViewInit { let targetAttr, sourceAttr; + // Always handle inline styles (if any). + this.handleInlineStyles(siteId).catch((error) => { + this.logger.error('Error treating inline styles.', this.element); + }); + if (tagName === 'A') { targetAttr = 'href'; sourceAttr = 'href'; @@ -81,9 +89,6 @@ export class CoreExternalContentDirective implements AfterViewInit { } } else { - // Unsupported tag. - this.logger.warn('Directive attached to non-supported tag: ' + tagName); - return; } @@ -217,4 +222,39 @@ export class CoreExternalContentDirective implements AfterViewInit { }); }); } + + /** + * Handle inline styles, trying to download referenced files. + * + * @param {string} siteId Site ID. + * @return {Promise} Promise resolved if the element is successfully treated. + */ + protected handleInlineStyles(siteId: string): Promise { + let inlineStyles = this.element.getAttribute('style'); + + if (!inlineStyles) { + return Promise.resolve(); + } + + let urls = inlineStyles.match(/https?:\/\/[^"'\) ;]*/g); + if (!urls || !urls.length) { + return Promise.resolve(); + } + + const promises = []; + urls = this.utils.uniqueArray(urls); // Remove duplicates. + + urls.forEach((url) => { + promises.push(this.filepoolProvider.getUrlByUrl(siteId, url, this.component, this.componentId, 0, true, true) + .then((finalUrl) => { + + this.logger.debug('Using URL ' + finalUrl + ' for ' + url + ' in inline styles'); + inlineStyles = inlineStyles.replace(new RegExp(url, 'gi'), finalUrl); + })); + }); + + return this.utils.allPromises(promises).then(() => { + this.element.setAttribute('style', inlineStyles); + }); + } } diff --git a/src/directives/format-text.ts b/src/directives/format-text.ts index acaed8032..7efa99f04 100644 --- a/src/directives/format-text.ts +++ b/src/directives/format-text.ts @@ -87,7 +87,7 @@ export class CoreFormatTextDirective implements OnChanges { protected addExternalContent(element: HTMLElement): void { // Angular 2 doesn't let adding directives dynamically. Create the CoreExternalContentDirective manually. const extContent = new CoreExternalContentDirective( element, this.loggerProvider, this.filepoolProvider, - this.platform, this.sitesProvider, this.domUtils, this.urlUtils, this.appProvider); + this.platform, this.sitesProvider, this.domUtils, this.urlUtils, this.appProvider, this.utils); extContent.component = this.component; extContent.componentId = this.componentId; @@ -313,7 +313,8 @@ export class CoreFormatTextDirective implements OnChanges { audios, videos, iframes, - buttons; + buttons, + elementsWithInlineStyles; div.innerHTML = formatted; images = Array.from(div.querySelectorAll('img')); @@ -322,6 +323,7 @@ export class CoreFormatTextDirective implements OnChanges { videos = Array.from(div.querySelectorAll('video')); iframes = Array.from(div.querySelectorAll('iframe')); buttons = Array.from(div.querySelectorAll('.button')); + elementsWithInlineStyles = Array.from(div.querySelectorAll('*[style]')); // Walk through the content to find the links and add our directive to it. // Important: We need to look for links first because in 'img' we add new links without core-link. @@ -370,6 +372,15 @@ export class CoreFormatTextDirective implements OnChanges { } }); + // Handle inline styles. + elementsWithInlineStyles.forEach((el: HTMLElement) => { + // Only add external content for tags that haven't been treated already. + if (el.tagName != 'A' && el.tagName != 'IMG' && el.tagName != 'AUDIO' && el.tagName != 'VIDEO' + && el.tagName != 'SOURCE' && el.tagName != 'TRACK') { + this.addExternalContent(el); + } + }); + return div; }); }