MOBILE-2310 course: Implement course module component

main
Dani Palou 2018-01-02 08:47:21 +01:00
parent c14bc38569
commit 3c8ad5b7b3
12 changed files with 583 additions and 24 deletions

View File

@ -19,10 +19,12 @@ import { TranslateModule } from '@ngx-translate/core';
import { CoreComponentsModule } from '../../../components/components.module';
import { CoreDirectivesModule } from '../../../directives/directives.module';
import { CoreCourseFormatComponent } from './format/format';
import { CoreCourseModuleComponent } from './module/module';
@NgModule({
declarations: [
CoreCourseFormatComponent
CoreCourseFormatComponent,
CoreCourseModuleComponent
],
imports: [
CommonModule,
@ -34,7 +36,8 @@ import { CoreCourseFormatComponent } from './format/format';
providers: [
],
exports: [
CoreCourseFormatComponent
CoreCourseFormatComponent,
CoreCourseModuleComponent
]
})
export class CoreCourseComponentsModule {}

View File

@ -57,7 +57,9 @@
<core-format-text [text]="section.summary" maxHeight="60"></core-format-text>
</ion-item>
<!-- <mm-course-module ng-if="module.visibleoncoursepage !== 0" class="item item-complex" ng-repeat="module in section.modules" module="module" completion-changed="completionChanged"></mm-course-module> -->
<ng-container *ngFor="let module of section.modules">
<core-course-module *ngIf="module.visibleoncoursepage !== 0" [module]="module" [courseId]="course.id" (completionChanged)="completionChanged.emit()"></core-course-module>
</ng-container>
</section>
</ng-template>

View File

@ -13,7 +13,7 @@
// limitations under the License.
import { Component, Input, OnInit, OnChanges, ViewContainerRef, ComponentFactoryResolver, ViewChild, ChangeDetectorRef,
SimpleChange } from '@angular/core';
SimpleChange, Output, EventEmitter } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { CoreLoggerProvider } from '../../../../providers/logger';
import { CoreCourseProvider } from '../../../course/providers/course';
@ -27,7 +27,7 @@ import { CoreCourseFormatDelegate } from '../../../course/providers/format-deleg
*
* Example usage:
*
* <core-course-format [course]="course" [sections]="sections"></core-course-format>
* <core-course-format [course]="course" [sections]="sections" (completionChanged)="onCompletionChange()"></core-course-format>
*/
@Component({
selector: 'core-course-format',
@ -36,6 +36,7 @@ import { CoreCourseFormatDelegate } from '../../../course/providers/format-deleg
export class CoreCourseFormatComponent implements OnInit, OnChanges {
@Input() course: any; // The course to render.
@Input() sections: any[]; // List of course sections.
@Output() completionChanged?: EventEmitter<void>; // Will emit an event when any module completion changes.
// Get the containers where to inject dynamic components. We use a setter because they might be inside a *ngIf.
@ViewChild('courseFormat', { read: ViewContainerRef }) set courseFormat(el: ViewContainerRef) {
@ -74,6 +75,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges {
private factoryResolver: ComponentFactoryResolver, private cdr: ChangeDetectorRef) {
this.logger = logger.getInstance('CoreCourseFormatComponent');
this.selectOptions.title = translate.instant('core.course.sections');
this.completionChanged = new EventEmitter();
}
/**

View File

@ -0,0 +1,22 @@
<a *ngIf="module && module.visibleoncoursepage !== 0" ion-item text-wrap id="mm-course-module-{{module.id}}" class="mm-course-module-handler {{module.handlerData.class}}" (click)="moduleClicked($event)" [ngClass]="{'item-media': module.handlerData.icon, 'mm-not-clickable': !module.handlerData.action || !module.uservisible === false, 'item-dimmed': module.visible === 0 || module.uservisible === false}" title="{{ module.handlerData.title }}">
<img item-start *ngIf="module.handlerData.icon" [src]="module.handlerData.icon" alt="" role="presentation">
<core-format-text [text]="module.handlerData.title"></core-format-text>
<div item-end *ngIf="module.uservisible !== false && ((module.handlerData.buttons && module.handlerData.buttons.length > 0) || spinner || module.completionstatus)" class="buttons mm-module-buttons" [ngClass]="{'mm-button-completion': module.completionstatus}">
<!-- <mm-completion ng-if="module.completionstatus" completion="module.completionstatus" module-name="module.name" after-change="completionChanged"></mm-completion> -->
<button ion-button icon-only clear *ngFor="let button of module.handlerData.buttons" [hidden]="button.hidden" (click)="button.action($event, module, courseId)" class="mm-animate-show-hide" [attr.aria-label]="button.label | translate">
<ion-icon [name]="button.icon" [ios]="button.iosIcon || ''" [md]="button.mdIcon || ''"></ion-icon>
</button>
<ion-spinner *ngIf="module.handlerData.spinner" class="mm-animate-show-hide"></ion-spinner>
</div>
<div *ngIf="module.visible === 0 || module.availabilityinfo">
<ion-badge item-end *ngIf="module.visible === 0">{{ 'core.course.hiddenfromstudents' | translate }}</ion-badge>
<ion-badge item-end *ngIf="module.availabilityinfo"><core-format-text [text]="module.availabilityinfo"></core-format-text></ion-badge>
</div>
<core-format-text *ngIf="module.description" maxHeight="80" [text]="module.description"></core-format-text>
</a>

View File

@ -0,0 +1,57 @@
// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
/**
* Component to display a module entry in a list of modules.
*
* Example usage:
*
* <core-course-module [module]="module" [courseId]="courseId" (completionChanged)="onCompletionChange()"></core-course-module>
*/
@Component({
selector: 'core-course-module',
templateUrl: 'module.html'
})
export class CoreCourseModuleComponent implements OnInit {
@Input() module: any; // The module to render.
@Input() courseId: number; // The course the module belongs to.
@Output() completionChanged?: EventEmitter<void>; // Will emit an event when the module completion changes.
constructor() {
this.completionChanged = new EventEmitter();
}
/**
* Component being initialized.
*/
ngOnInit() {
// Handler data must be defined. If it isn't, set it to prevent errors.
if (this.module && !this.module.handlerData) {
this.module.handlerData = {};
}
}
/**
* Function called when the module is clicked.
*
* @param {Event} event Click event.
*/
moduleClicked(event: Event) {
if (this.module.uservisible !== false && this.module.handlerData.action) {
this.module.handlerData.action(event, this.module, this.courseId);
}
}
}

View File

@ -16,6 +16,7 @@ import { NgModule } from '@angular/core';
import { CoreCourseProvider } from './providers/course';
import { CoreCourseHelperProvider } from './providers/helper';
import { CoreCourseFormatDelegate } from './providers/format-delegate';
import { CoreCourseModuleDelegate } from './providers/module-delegate';
@NgModule({
declarations: [],
@ -24,7 +25,8 @@ import { CoreCourseFormatDelegate } from './providers/format-delegate';
providers: [
CoreCourseProvider,
CoreCourseHelperProvider,
CoreCourseFormatDelegate
CoreCourseFormatDelegate,
CoreCourseModuleDelegate
],
exports: []
})

View File

@ -19,6 +19,6 @@
<a ion-button class="tab-item">{{ 'core.course.contents' || translate }}</a>
<a *ngFor="let handler of courseHandlers" ion-button class="tab-item">{{ handler.data.title || translate }}</a>
</div>
<core-course-format [course]="course" [sections]="sections"></core-course-format>
<core-course-format [course]="course" [sections]="sections" (completionChanged)="onCompletionChange()"></core-course-format>
</core-loading>
</ion-content>

View File

@ -12,9 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Component } from '@angular/core';
import { IonicPage, NavParams } from 'ionic-angular';
import { Component, ViewChild, OnDestroy } from '@angular/core';
import { IonicPage, NavParams, Content } from 'ionic-angular';
import { TranslateService } from '@ngx-translate/core';
import { CoreEventsProvider } from '../../../../providers/events';
import { CoreDomUtilsProvider } from '../../../../providers/utils/dom';
import { CoreTextUtilsProvider } from '../../../../providers/utils/text';
import { CoreCourseProvider } from '../../providers/course';
@ -30,19 +31,31 @@ import { CoreCoursesDelegate } from '../../../courses/providers/delegate';
selector: 'page-core-course-section',
templateUrl: 'section.html',
})
export class CoreCourseSectionPage {
export class CoreCourseSectionPage implements OnDestroy {
@ViewChild(Content) content: Content;
title: string;
course: any;
sections: any[];
courseHandlers: any[];
dataLoaded: boolean;
protected moduleId;
protected completionObserver;
constructor(navParams: NavParams, private courseProvider: CoreCourseProvider, private domUtils: CoreDomUtilsProvider,
private courseFormatDelegate: CoreCourseFormatDelegate, private coursesDelegate: CoreCoursesDelegate,
private translate: TranslateService, private courseHelper: CoreCourseHelperProvider,
private textUtils: CoreTextUtilsProvider) {
private textUtils: CoreTextUtilsProvider, eventsProvider: CoreEventsProvider) {
this.course = navParams.get('course');
this.title = courseFormatDelegate.getCourseTitle(this.course);
this.moduleId = navParams.get('moduleId');
this.completionObserver = eventsProvider.on(CoreEventsProvider.COMPLETION_MODULE_VIEWED, (data) => {
if (data && data.courseId == this.course.id) {
this.refreshAfterCompletionChange();
}
});
}
/**
@ -51,6 +64,7 @@ export class CoreCourseSectionPage {
ionViewDidLoad() {
this.loadData().finally(() => {
this.dataLoaded = true;
delete this.moduleId; // Only load module automatically the first time.
});
}
@ -75,6 +89,8 @@ export class CoreCourseSectionPage {
promises.push(promise.then((completionStatus) => {
// Get all the sections.
promises.push(this.courseProvider.getSections(this.course.id, false, true).then((sections) => {
this.courseHelper.addHandlerDataForModules(sections, this.course.id, this.moduleId, completionStatus);
// Format the name of each section and check if it has content.
this.sections = sections.map((section) => {
this.textUtils.formatText(section.name.trim(), true, true).then((name) => {
@ -125,4 +141,40 @@ export class CoreCourseSectionPage {
});
});
}
/**
* The completion of any of the modules have changed.
*/
onCompletionChange() {
this.courseProvider.invalidateSections(this.course.id).finally(() => {
this.refreshAfterCompletionChange();
});
}
/**
* Refresh list after a completion change since there could be new activities.
*/
protected refreshAfterCompletionChange() {
// Save scroll position to restore it once done.
let scrollElement = this.content.getScrollElement(),
scrollTop = scrollElement.scrollTop || 0,
scrollLeft = scrollElement.scrollLeft || 0;
this.dataLoaded = false;
this.content.scrollToTop(); // Scroll top so the spinner is seen.
this.loadData().finally(() => {
this.dataLoaded = true;
this.content.scrollTo(scrollLeft, scrollTop);
});
}
/**
* Page destroyed.
*/
ngOnDestroy() {
if (this.completionObserver) {
this.completionObserver.off();
}
}
}

View File

@ -13,7 +13,9 @@
// limitations under the License.
import { Injectable } from '@angular/core';
import { CoreDomUtilsProvider } from '../../../providers/utils/dom';
import { CoreCourseProvider } from './course';
import { CoreCourseModuleDelegate } from './module-delegate';
/**
* Helper to gather some common course functions.
@ -21,7 +23,84 @@ import { CoreCourseProvider } from './course';
@Injectable()
export class CoreCourseHelperProvider {
constructor() {}
constructor(private courseProvider: CoreCourseProvider, private domUtils: CoreDomUtilsProvider,
private moduleDelegate: CoreCourseModuleDelegate) {}
/**
* This function treats every module on the sections provided to load the handler data, treat completion
* and navigate to a module page if required. It also returns if sections has content.
*
* @param {any[]} sections List of sections to treat modules.
* @param {number} courseId Course ID of the modules.
* @param {number} [moduleId] Module to navigate to if needed.
* @param {any[]} [completionStatus] List of completion status.
* @return {boolean} Whether the sections have content.
*/
addHandlerDataForModules(sections: any[], courseId: number, moduleId?: number, completionStatus?: any) {
let hasContent = false;
sections.forEach((section) => {
if (!section || !this.sectionHasContent(section) || !section.modules) {
return;
}
hasContent = true;
section.modules.forEach((module) => {
module.handlerData = this.moduleDelegate.getModuleDataFor(module.modname, module, courseId, section.id);
if (completionStatus && typeof completionStatus[module.id] != 'undefined') {
// Check if activity has completions and if it's marked.
module.completionstatus = completionStatus[module.id];
module.completionstatus.courseId = courseId;
}
if (module.id == moduleId) {
// This is the module we're looking for. Open it.
module.handlerData.action(new Event('click'), module, courseId);
}
});
});
return hasContent;
}
/**
* Get the course ID from a module instance ID, showing an error message if it can't be retrieved.
*
* @param {number} id Instance ID.
* @param {string} module Name of the module. E.g. 'glossary'.
* @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<number>} Promise resolved with the module's course ID.
*/
getModuleCourseIdByInstance(id: number, module: any, siteId?: string) : Promise<number> {
return this.courseProvider.getModuleBasicInfoByInstance(id, module, siteId).then((cm) => {
return cm.course;
}).catch((error) => {
this.domUtils.showErrorModalDefault(error, 'core.course.errorgetmodule', true);
return Promise.reject(null);
});
}
/**
* Given a list of sections, returns the list of modules in the sections.
*
* @param {any[]} sections Sections.
* @return {any[]} Modules.
*/
getSectionsModules(sections: any[]) : any[] {
if (!sections || !sections.length) {
return [];
}
let modules = [];
sections.forEach((section) => {
if (section.modules) {
modules = modules.concat(section.modules);
}
});
return modules;
}
/**
* Check if a section has content.

View File

@ -0,0 +1,331 @@
// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Injectable } from '@angular/core';
import { CoreEventsProvider } from '../../../providers/events';
import { CoreLoggerProvider } from '../../../providers/logger';
import { CoreSitesProvider } from '../../../providers/sites';
import { CoreCourseProvider } from './course';
import { CoreSite } from '../../../classes/site';
/**
* Interface that all course module handlers should implement.
*/
export interface CoreCourseModuleHandler {
/**
* A name to identify the addon.
* @type {string}
*/
name: string;
/**
* Name of the module. It should match the "modname" of the module returned in core_course_get_contents.
* @type {string}
*/
modname: string;
/**
* Whether or not the handler is enabled on a site level.
*
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
*/
isEnabled(): boolean|Promise<boolean>;
/**
* Get the data required to display the module in the course contents view.
*
* @param {any} module The module object.
* @param {number} courseId The course ID.
* @param {number} sectionId The section ID.
* @return {CoreCourseModuleHandlerData} Data to render the module.
*/
getData(module: any, courseId: number, sectionId: number) : CoreCourseModuleHandlerData;
};
/**
* Data needed to render the module in course contents.
*/
export interface CoreCourseModuleHandlerData {
/**
* The title to display in the module.
* @type {string}
*/
title: string;
/**
* The image to use as icon (path to the image).
* @type {string}
*/
icon?: string;
/**
* The class to assign to the item.
* @type {string}
*/
class?: string;
/**
* The buttons to display in the module item.
* @type {CoreCourseModuleHandlerButton[]}
*/
buttons?: CoreCourseModuleHandlerButton[];
/**
* Whether to display a spinner in the module item.
* @type {boolean}
*/
spinner?: boolean;
/**
* Action to perform when the module is clicked.
*
* @param {Event} event The click event.
* @param {any} module The module object.
* @param {number} courseId The course ID.
*/
action?(event: Event, module: any, courseId: number) : void;
};
/**
* A button to display in a module item.
*/
export interface CoreCourseModuleHandlerButton {
/**
* The label to add to the button.
* @type {string}
*/
label: string;
/**
* The name of the button icon.
* @type {string}
*/
icon: string;
/**
* Whether the button should be hidden.
* @type {boolean}
*/
hidden?: boolean;
/**
* The name of the button icon to use in iOS instead of "icon".
* @type {string}
*/
iosIcon?: string;
/**
* The name of the button icon to use in MaterialDesign instead of "icon".
* @type {string}
*/
mdIcon?: string;
/**
* Action to perform when the button is clicked.
*
* @param {Event} event The click event.
* @param {any} module The module object.
* @param {number} courseId The course ID.
*/
action?(event: Event, module: any, courseId: number) : void;
};
/**
* Delegate to register module handlers.
*/
@Injectable()
export class CoreCourseModuleDelegate {
protected logger;
protected handlers: {[s: string]: CoreCourseModuleHandler} = {}; // All registered handlers.
protected enabledHandlers: {[s: string]: CoreCourseModuleHandler} = {}; // Handlers enabled for the current site.
protected lastUpdateHandlersStart: number;
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, eventsProvider: CoreEventsProvider,
private courseProvider: CoreCourseProvider) {
this.logger = logger.getInstance('CoreCourseModuleDelegate');
eventsProvider.on(CoreEventsProvider.LOGIN, this.updateHandlers.bind(this));
eventsProvider.on(CoreEventsProvider.SITE_UPDATED, this.updateHandlers.bind(this));
eventsProvider.on(CoreEventsProvider.REMOTE_ADDONS_LOADED, this.updateHandlers.bind(this));
}
/**
* Get the data required to display the module in the course contents view.
*
* @param {string} modname The name of the module type.
* @param {any} module The module object.
* @param {number} courseId The course ID.
* @param {number} sectionId The section ID.
* @return {CoreCourseModuleHandlerData} Data to render the module.
*/
getModuleDataFor(modname: string, module: any, courseId: number, sectionId: number) : CoreCourseModuleHandlerData {
if (typeof this.enabledHandlers[modname] != 'undefined') {
return this.enabledHandlers[modname].getData(module, courseId, sectionId);
}
// Return the default data.
let defaultData: CoreCourseModuleHandlerData = {
icon: this.courseProvider.getModuleIconSrc(module.modname),
title: module.name,
class: 'core-course-default-handler core-course-module-' + module.modname + '-handler',
action: (event: Event, module: any, courseId: number) => {
event.preventDefault();
event.stopPropagation();
// @todo: Default content.
}
};
if (module.url) {
defaultData.buttons = [{
icon: 'open',
label: 'core.openinbrowser',
action: (e: Event) => {
e.preventDefault();
e.stopPropagation();
this.sitesProvider.getCurrentSite().openInBrowserWithAutoLoginIfSameSite(module.url);
}
}];
}
return defaultData;
};
/**
* Check if a module has a registered handler (not necessarily enabled).
*
* @param {string} modname The name of the module type.
* @return {boolean} If the controller is installed or not.
*/
hasHandler(modname: string) : boolean {
return typeof this.handlers[modname] !== 'undefined';
}
/**
* Check if a certain module type is disabled in a site.
*
* @param {string} modname The name of the module type.
* @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<boolean>} Promise resolved with boolean: whether module is disabled.
*/
isModuleDisabled(modname: string, siteId?: string) : Promise<boolean> {
return this.sitesProvider.getSite(siteId).then((site) => {
return this.isModuleDisabledInSite(modname, site);
});
}
/**
* Check if a certain module type is disabled in a site.
*
* @param {string} modname The name of the module type.
* @param {CoreSite} [site] Site. If not defined, use current site.
* @return {boolean} Whether module is disabled.
*/
isModuleDisabledInSite(modname: string, site?: CoreSite) : boolean {
site = site || this.sitesProvider.getCurrentSite();
if (typeof this.handlers[modname] != 'undefined') {
return site.isFeatureDisabled('$mmCourseDelegate_' + this.handlers[modname].name);
}
return false;
}
/**
* Check if a time belongs to the last update handlers call.
* This is to handle the cases where updateHandlers don't finish in the same order as they're called.
*
* @param {number} time Time to check.
* @return {boolean} Whether it's the last call.
*/
isLastUpdateCall(time: number) : boolean {
if (!this.lastUpdateHandlersStart) {
return true;
}
return time == this.lastUpdateHandlersStart;
}
/**
* Register a handler.
*
* @param {CoreCourseModuleHandler} handler The handler to register.
* @return {boolean} True if registered successfully, false otherwise.
*/
registerHandler(handler: CoreCourseModuleHandler) : boolean {
if (typeof this.handlers[handler.modname] !== 'undefined') {
this.logger.log('There is an addon named \'' + this.handlers[handler.modname].name +
'\' already registered as handler for ' + handler.modname);
return false;
}
this.logger.log(`Registered addon '${handler.name}' for '${handler.modname}'`);
this.handlers[handler.modname] = handler;
return true;
}
/**
* Update the handler for the current site.
*
* @param {CoreCourseModuleHandler} handler The handler to check.
* @param {number} time Time this update process started.
* @return {Promise<void>} Resolved when done.
*/
protected updateHandler(handler: CoreCourseModuleHandler, time: number) : Promise<void> {
let promise,
siteId = this.sitesProvider.getCurrentSiteId(),
currentSite = this.sitesProvider.getCurrentSite();
if (!this.sitesProvider.isLoggedIn()) {
promise = Promise.reject(null);
} else if (currentSite.isFeatureDisabled('$mmCourseDelegate_' + handler.name)) {
promise = Promise.resolve(false);
} else {
promise = Promise.resolve(handler.isEnabled());
}
// Checks if the handler is enabled.
return promise.catch(() => {
return false;
}).then((enabled: boolean) => {
// Verify that this call is the last one that was started.
if (this.isLastUpdateCall(time) && this.sitesProvider.getCurrentSiteId() === siteId) {
if (enabled) {
this.enabledHandlers[handler.modname] = handler;
} else {
delete this.enabledHandlers[handler.modname];
}
}
});
}
/**
* Update the handlers for the current site.
*
* @return {Promise<any>} Resolved when done.
*/
protected updateHandlers() : Promise<any> {
let promises = [],
now = Date.now();
this.logger.debug('Updating handlers for current site.');
this.lastUpdateHandlersStart = now;
// Loop over all the handlers.
for (let name in this.handlers) {
promises.push(this.updateHandler(this.handlers[name], now));
}
return Promise.all(promises).catch(() => {
// Never reject.
});
}
}

View File

@ -103,16 +103,14 @@ export class CoreFormatTextDirective implements OnChanges {
}
/**
* Create a container for an image to adapt its width.
* Wrap an image with a container to adapt its width and, if needed, add an anchor to view it in full size.
*
* @param {number} elWidth Width of the directive's element.
* @param {HTMLElement} img Image to adapt.
* @return {HTMLElement} Container.
*/
protected createMagnifyingGlassContainer(elWidth: number, img: HTMLElement) : HTMLElement {
// Check if image width has been adapted. If so, add an icon to view the image at full size.
protected adaptImage(elWidth: number, img: HTMLElement) : void {
let imgWidth = this.getElementWidth(img),
// Wrap the image in a new div with position relative.
// Element to wrap the image.
container = document.createElement('span');
container.classList.add('core-adapted-img-container');
@ -122,9 +120,11 @@ export class CoreFormatTextDirective implements OnChanges {
} else if (img.classList.contains('atto_image_button_left')) {
container.classList.add('atto_image_button_left');
}
container.appendChild(img);
this.domUtils.wrapElement(img, container);
if (imgWidth > elWidth) {
// The image has been adapted, add an anchor to view it in full size.
let imgSrc = this.textUtils.escapeHTML(img.getAttribute('src')),
label = this.textUtils.escapeHTML(this.translate.instant('core.openfullimage'));
@ -132,8 +132,6 @@ export class CoreFormatTextDirective implements OnChanges {
container.innerHTML += '<a href="#" class="core-image-viewer-icon" core-image-viewer img="' + imgSrc +
'" aria-label="' + label + '"><ion-icon name="search"></ion-icon></a>';
}
return container;
}
/**
@ -233,7 +231,6 @@ export class CoreFormatTextDirective implements OnChanges {
// Apply format text function.
return this.textUtils.formatText(this.text, this.utils.isTrueOrOne(this.clean), this.utils.isTrueOrOne(this.singleLine));
}).then((formatted) => {
let div = document.createElement('div'),
canTreatVimeo = site && site.isVersionGreaterEqualThan(['3.3.4', '3.4']),
images,
@ -271,9 +268,7 @@ export class CoreFormatTextDirective implements OnChanges {
this.addMediaAdaptClass(img);
this.addExternalContent(img);
if (this.utils.isTrueOrOne(this.adaptImg)) {
// Create a container for the image and use it instead of the image.
let container = this.createMagnifyingGlassContainer(elWidth, img);
div.replaceChild(container, img);
this.adaptImage(elWidth, img);
}
});
}

View File

@ -43,6 +43,7 @@ export class CoreDomUtilsProvider {
* @return {string} Result message.
*/
private addFormatTextIfNeeded(message: string) : string {
// @todo
if (this.textUtils.hasHTMLTags(message)) {
return '<core-format-text watch="true">' + message + '</core-format-text>';
}
@ -838,4 +839,17 @@ export class CoreDomUtilsProvider {
return el && !el.disabled && (el.tagName.toLowerCase() == 'textarea' ||
(el.tagName.toLowerCase() == 'input' && this.inputSupportKeyboard.indexOf(el.type) != -1));
}
/**
* Wrap an HTMLElement with another element.
*
* @param {HTMLElement} el The element to wrap.
* @param {HTMLElement} wrapper Wrapper.
*/
wrapElement(el: HTMLElement, wrapper: HTMLElement) : void {
// Insert the wrapper before the element.
el.parentNode.insertBefore(wrapper, el);
// Now move the element into the wrapper.
wrapper.appendChild(el);
}
}