diff --git a/src/core/course/components/components.module.ts b/src/core/course/components/components.module.ts index 5dd16ac0b..693e245b8 100644 --- a/src/core/course/components/components.module.ts +++ b/src/core/course/components/components.module.ts @@ -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 {} diff --git a/src/core/course/components/format/format.html b/src/core/course/components/format/format.html index 662a409a4..48fa51547 100644 --- a/src/core/course/components/format/format.html +++ b/src/core/course/components/format/format.html @@ -57,7 +57,9 @@ - + + + diff --git a/src/core/course/components/format/format.ts b/src/core/course/components/format/format.ts index c0fce8c58..f341c89a9 100644 --- a/src/core/course/components/format/format.ts +++ b/src/core/course/components/format/format.ts @@ -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: * - * + * */ @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; // 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(); } /** diff --git a/src/core/course/components/module/module.html b/src/core/course/components/module/module.html new file mode 100644 index 000000000..2b79e0e7e --- /dev/null +++ b/src/core/course/components/module/module.html @@ -0,0 +1,22 @@ + + + + + + +
+ + + + + +
+ +
+ {{ 'core.course.hiddenfromstudents' | translate }} + +
+ +
\ No newline at end of file diff --git a/src/core/course/components/module/module.ts b/src/core/course/components/module/module.ts new file mode 100644 index 000000000..2fbf2a670 --- /dev/null +++ b/src/core/course/components/module/module.ts @@ -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: + * + * + */ +@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; // 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); + } + } +} diff --git a/src/core/course/course.module.ts b/src/core/course/course.module.ts index e54c6b8b1..a9e9baf1a 100644 --- a/src/core/course/course.module.ts +++ b/src/core/course/course.module.ts @@ -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: [] }) diff --git a/src/core/course/pages/section/section.html b/src/core/course/pages/section/section.html index e0d033b3a..cfc1c9752 100644 --- a/src/core/course/pages/section/section.html +++ b/src/core/course/pages/section/section.html @@ -19,6 +19,6 @@ {{ 'core.course.contents' || translate }} {{ handler.data.title || translate }} - + diff --git a/src/core/course/pages/section/section.ts b/src/core/course/pages/section/section.ts index 337eb3584..0c8c3c459 100644 --- a/src/core/course/pages/section/section.ts +++ b/src/core/course/pages/section/section.ts @@ -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(); + } + } } diff --git a/src/core/course/providers/helper.ts b/src/core/course/providers/helper.ts index cf9ac5900..0c30c00f4 100644 --- a/src/core/course/providers/helper.ts +++ b/src/core/course/providers/helper.ts @@ -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} Promise resolved with the module's course ID. + */ + getModuleCourseIdByInstance(id: number, module: any, siteId?: string) : Promise { + 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. diff --git a/src/core/course/providers/module-delegate.ts b/src/core/course/providers/module-delegate.ts new file mode 100644 index 000000000..452021076 --- /dev/null +++ b/src/core/course/providers/module-delegate.ts @@ -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} True or promise resolved with true if enabled. + */ + isEnabled(): boolean|Promise; + + /** + * 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} Promise resolved with boolean: whether module is disabled. + */ + isModuleDisabled(modname: string, siteId?: string) : Promise { + 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} Resolved when done. + */ + protected updateHandler(handler: CoreCourseModuleHandler, time: number) : Promise { + 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} Resolved when done. + */ + protected updateHandlers() : Promise { + 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. + }); + } +} diff --git a/src/directives/format-text.ts b/src/directives/format-text.ts index d71e2141b..19fd36c34 100644 --- a/src/directives/format-text.ts +++ b/src/directives/format-text.ts @@ -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 += ''; } - - 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); } }); } diff --git a/src/providers/utils/dom.ts b/src/providers/utils/dom.ts index 5ace19ffb..92b860324 100644 --- a/src/providers/utils/dom.ts +++ b/src/providers/utils/dom.ts @@ -43,6 +43,7 @@ export class CoreDomUtilsProvider { * @return {string} Result message. */ private addFormatTextIfNeeded(message: string) : string { + // @todo if (this.textUtils.hasHTMLTags(message)) { return '' + message + ''; } @@ -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); + } }