MOBILE-3074 format-text: Fix size calculation when content has images
parent
1944f46fb5
commit
90f340aaa5
|
@ -12,7 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Directive, Input, AfterViewInit, ElementRef, OnChanges, SimpleChange } from '@angular/core';
|
import { Directive, Input, AfterViewInit, ElementRef, OnChanges, SimpleChange, Output, EventEmitter } from '@angular/core';
|
||||||
import { Platform } from 'ionic-angular';
|
import { Platform } from 'ionic-angular';
|
||||||
import { CoreAppProvider } from '@providers/app';
|
import { CoreAppProvider } from '@providers/app';
|
||||||
import { CoreLoggerProvider } from '@providers/logger';
|
import { CoreLoggerProvider } from '@providers/logger';
|
||||||
|
@ -43,6 +43,7 @@ export class CoreExternalContentDirective implements AfterViewInit, OnChanges {
|
||||||
@Input() href?: string;
|
@Input() href?: string;
|
||||||
@Input('target-src') targetSrc?: string;
|
@Input('target-src') targetSrc?: string;
|
||||||
@Input() poster?: string;
|
@Input() poster?: string;
|
||||||
|
@Output() onLoad = new EventEmitter(); // Emitted when content is loaded. Only for images.
|
||||||
|
|
||||||
protected element: HTMLElement;
|
protected element: HTMLElement;
|
||||||
protected logger;
|
protected logger;
|
||||||
|
@ -225,7 +226,17 @@ export class CoreExternalContentDirective implements AfterViewInit, OnChanges {
|
||||||
// The browser does not catch changes in SRC, we need to add a new source.
|
// The browser does not catch changes in SRC, we need to add a new source.
|
||||||
this.addSource(finalUrl);
|
this.addSource(finalUrl);
|
||||||
} else {
|
} else {
|
||||||
|
if (tagName === 'IMG') {
|
||||||
|
const listener = (): void => {
|
||||||
|
this.element.removeEventListener('load', listener);
|
||||||
|
this.element.removeEventListener('error', listener);
|
||||||
|
this.onLoad.emit();
|
||||||
|
};
|
||||||
|
this.element.addEventListener('load', listener);
|
||||||
|
this.element.addEventListener('error', listener);
|
||||||
|
}
|
||||||
this.element.setAttribute(targetAttr, finalUrl);
|
this.element.setAttribute(targetAttr, finalUrl);
|
||||||
|
this.element.setAttribute('data-original-' + targetAttr, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set events to download big files (not downloaded automatically).
|
// Set events to download big files (not downloaded automatically).
|
||||||
|
|
|
@ -90,8 +90,9 @@ export class CoreFormatTextDirective implements OnChanges {
|
||||||
* Apply CoreExternalContentDirective to a certain element.
|
* Apply CoreExternalContentDirective to a certain element.
|
||||||
*
|
*
|
||||||
* @param {HTMLElement} element Element to add the attributes to.
|
* @param {HTMLElement} element Element to add the attributes to.
|
||||||
|
* @return {CoreExternalContentDirective} External content instance.
|
||||||
*/
|
*/
|
||||||
protected addExternalContent(element: HTMLElement): void {
|
protected addExternalContent(element: HTMLElement): CoreExternalContentDirective {
|
||||||
// Angular 2 doesn't let adding directives dynamically. Create the CoreExternalContentDirective manually.
|
// Angular 2 doesn't let adding directives dynamically. Create the CoreExternalContentDirective manually.
|
||||||
const extContent = new CoreExternalContentDirective(<any> element, this.loggerProvider, this.filepoolProvider,
|
const extContent = new CoreExternalContentDirective(<any> element, this.loggerProvider, this.filepoolProvider,
|
||||||
this.platform, this.sitesProvider, this.domUtils, this.urlUtils, this.appProvider, this.utils);
|
this.platform, this.sitesProvider, this.domUtils, this.urlUtils, this.appProvider, this.utils);
|
||||||
|
@ -105,6 +106,8 @@ export class CoreFormatTextDirective implements OnChanges {
|
||||||
extContent.poster = element.getAttribute('poster');
|
extContent.poster = element.getAttribute('poster');
|
||||||
|
|
||||||
extContent.ngAfterViewInit();
|
extContent.ngAfterViewInit();
|
||||||
|
|
||||||
|
return extContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -117,15 +120,13 @@ export class CoreFormatTextDirective implements OnChanges {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrap an image with a container to adapt its width and, if needed, add an anchor to view it in full size.
|
* Wrap an image with a container to adapt its width.
|
||||||
*
|
*
|
||||||
* @param {number} elWidth Width of the directive's element.
|
|
||||||
* @param {HTMLElement} img Image to adapt.
|
* @param {HTMLElement} img Image to adapt.
|
||||||
*/
|
*/
|
||||||
protected adaptImage(elWidth: number, img: HTMLElement): void {
|
protected adaptImage(img: HTMLElement): void {
|
||||||
const imgWidth = this.getElementWidth(img),
|
// Element to wrap the image.
|
||||||
// Element to wrap the image.
|
const container = document.createElement('span'),
|
||||||
container = document.createElement('span'),
|
|
||||||
originalWidth = img.attributes.getNamedItem('width');
|
originalWidth = img.attributes.getNamedItem('width');
|
||||||
|
|
||||||
const forcedWidth = parseInt(originalWidth && originalWidth.value);
|
const forcedWidth = parseInt(originalWidth && originalWidth.value);
|
||||||
|
@ -152,36 +153,48 @@ export class CoreFormatTextDirective implements OnChanges {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.domUtils.wrapElement(img, container);
|
this.domUtils.wrapElement(img, container);
|
||||||
|
|
||||||
if (imgWidth > elWidth) {
|
|
||||||
// The image has been adapted, add an anchor to view it in full size.
|
|
||||||
this.addMagnifyingGlass(container, img);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a magnifying glass icon to view an image at full size.
|
* Add magnifying glass icons to view adapted images at full size.
|
||||||
*
|
|
||||||
* @param {HTMLElement} container The container of the image.
|
|
||||||
* @param {HTMLElement} img The image.
|
|
||||||
*/
|
*/
|
||||||
addMagnifyingGlass(container: HTMLElement, img: HTMLElement): void {
|
addMagnifyingGlasses(): void {
|
||||||
const imgSrc = this.textUtils.escapeHTML(img.getAttribute('src')),
|
const imgs = Array.from(this.element.querySelectorAll('.core-adapted-img-container > img'));
|
||||||
|
if (!imgs.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If cannot calculate element's width, use viewport width to avoid false adapt image icons appearing.
|
||||||
|
const elWidth = this.getElementWidth(this.element) || window.innerWidth;
|
||||||
|
|
||||||
|
imgs.forEach((img: HTMLImageElement) => {
|
||||||
|
let imgWidth = parseInt(img.getAttribute('width'));
|
||||||
|
if (!imgWidth) {
|
||||||
|
// No width attribute, use real size.
|
||||||
|
imgWidth = img.naturalWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (imgWidth <= elWidth) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const imgSrc = this.textUtils.escapeHTML(img.getAttribute('data-original-src') || img.getAttribute('src')),
|
||||||
label = this.textUtils.escapeHTML(this.translate.instant('core.openfullimage')),
|
label = this.textUtils.escapeHTML(this.translate.instant('core.openfullimage')),
|
||||||
anchor = document.createElement('a');
|
anchor = document.createElement('a');
|
||||||
|
|
||||||
anchor.classList.add('core-image-viewer-icon');
|
anchor.classList.add('core-image-viewer-icon');
|
||||||
anchor.setAttribute('aria-label', label);
|
anchor.setAttribute('aria-label', label);
|
||||||
// Add an ion-icon item to apply the right styles, but the ion-icon component won't be executed.
|
// Add an ion-icon item to apply the right styles, but the ion-icon component won't be executed.
|
||||||
anchor.innerHTML = '<ion-icon name="search" class="icon icon-md ion-md-search"></ion-icon>';
|
anchor.innerHTML = '<ion-icon name="search" class="icon icon-md ion-md-search"></ion-icon>';
|
||||||
|
|
||||||
anchor.addEventListener('click', (e: Event) => {
|
anchor.addEventListener('click', (e: Event) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
this.domUtils.viewImage(imgSrc, img.getAttribute('alt'), this.component, this.componentId);
|
this.domUtils.viewImage(imgSrc, img.getAttribute('alt'), this.component, this.componentId);
|
||||||
|
});
|
||||||
|
|
||||||
|
img.parentNode.appendChild(anchor);
|
||||||
});
|
});
|
||||||
|
|
||||||
container.appendChild(anchor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -307,12 +320,8 @@ export class CoreFormatTextDirective implements OnChanges {
|
||||||
// Calculate the height now.
|
// Calculate the height now.
|
||||||
this.calculateHeight();
|
this.calculateHeight();
|
||||||
|
|
||||||
// Wait for images to load and calculate the height again if needed.
|
// Add magnifying glasses to images.
|
||||||
this.domUtils.waitForImages(this.element).then((hasImgToLoad) => {
|
this.addMagnifyingGlasses();
|
||||||
if (hasImgToLoad) {
|
|
||||||
this.calculateHeight();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!this.loadingChangedListener) {
|
if (!this.loadingChangedListener) {
|
||||||
// Recalculate the height if a parent core-loading displays the content.
|
// Recalculate the height if a parent core-loading displays the content.
|
||||||
|
@ -387,16 +396,14 @@ export class CoreFormatTextDirective implements OnChanges {
|
||||||
this.addExternalContent(anchor);
|
this.addExternalContent(anchor);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const externalImages: CoreExternalContentDirective[] = [];
|
||||||
if (images && images.length > 0) {
|
if (images && images.length > 0) {
|
||||||
// If cannot calculate element's width, use a medium number to avoid false adapt image icons appearing.
|
|
||||||
const elWidth = this.getElementWidth(this.element) || 100;
|
|
||||||
|
|
||||||
// Walk through the content to find images, and add our directive.
|
// Walk through the content to find images, and add our directive.
|
||||||
images.forEach((img: HTMLElement) => {
|
images.forEach((img: HTMLElement) => {
|
||||||
this.addMediaAdaptClass(img);
|
this.addMediaAdaptClass(img);
|
||||||
this.addExternalContent(img);
|
externalImages.push(this.addExternalContent(img));
|
||||||
if (this.utils.isTrueOrOne(this.adaptImg) && !img.classList.contains('icon')) {
|
if (this.utils.isTrueOrOne(this.adaptImg) && !img.classList.contains('icon')) {
|
||||||
this.adaptImage(elWidth, img);
|
this.adaptImage(img);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -445,7 +452,24 @@ export class CoreFormatTextDirective implements OnChanges {
|
||||||
|
|
||||||
this.domUtils.handleBootstrapTooltips(div);
|
this.domUtils.handleBootstrapTooltips(div);
|
||||||
|
|
||||||
return div;
|
// Wait for images to load.
|
||||||
|
let promise: Promise<any> = null;
|
||||||
|
if (externalImages.length) {
|
||||||
|
promise = Promise.all(externalImages.map((externalImage) => {
|
||||||
|
return new Promise((resolve): void => {
|
||||||
|
const subscription = externalImage.onLoad.subscribe(() => {
|
||||||
|
subscription.unsubscribe();
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
promise = Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
return promise.then(() => {
|
||||||
|
return div;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue