From 764309edd7b6345f2d0c13d55acc1ff3a189e6c4 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Wed, 3 Jan 2018 10:36:07 +0100 Subject: [PATCH] MOBILE-2310 core: Implement image viewer --- .../overview-events/overview-events.ts | 6 ++- src/core/viewer/pages/image/image.html | 14 ++++++ src/core/viewer/pages/image/image.module.ts | 31 ++++++++++++ src/core/viewer/pages/image/image.ts | 48 +++++++++++++++++++ src/directives/format-text.ts | 32 ++++++++++--- src/providers/utils/dom.ts | 40 +++++++++++++++- 6 files changed, 161 insertions(+), 10 deletions(-) create mode 100644 src/core/viewer/pages/image/image.html create mode 100644 src/core/viewer/pages/image/image.module.ts create mode 100644 src/core/viewer/pages/image/image.ts diff --git a/src/core/courses/components/overview-events/overview-events.ts b/src/core/courses/components/overview-events/overview-events.ts index fd566c4fa..df8db57cb 100644 --- a/src/core/courses/components/overview-events/overview-events.ts +++ b/src/core/courses/components/overview-events/overview-events.ts @@ -17,6 +17,7 @@ import { CoreSitesProvider } from '../../../../providers/sites'; import { CoreDomUtilsProvider } from '../../../../providers/utils/dom'; import { CoreTextUtilsProvider } from '../../../../providers/utils/text'; import { CoreUtilsProvider } from '../../../../providers/utils/utils'; +import { CoreCourseProvider } from '../../../course/providers/course'; import * as moment from 'moment'; /** @@ -41,7 +42,8 @@ export class CoreCoursesOverviewEventsComponent implements OnChanges { future: any[] = []; constructor(private utils: CoreUtilsProvider, private textUtils: CoreTextUtilsProvider, - private domUtils: CoreDomUtilsProvider, private sitesProvider: CoreSitesProvider) { + private domUtils: CoreDomUtilsProvider, private sitesProvider: CoreSitesProvider, + private courseProvider: CoreCourseProvider) { this.loadMore = new EventEmitter(); } @@ -73,7 +75,7 @@ export class CoreCoursesOverviewEventsComponent implements OnChanges { return start <= event.timesort; }).map((event) => { - // @todo: event.iconUrl = this.courseProvider.getModuleIconSrc(event.icon.component); + event.iconUrl = this.courseProvider.getModuleIconSrc(event.icon.component); return event; }); } diff --git a/src/core/viewer/pages/image/image.html b/src/core/viewer/pages/image/image.html new file mode 100644 index 000000000..3783c5d38 --- /dev/null +++ b/src/core/viewer/pages/image/image.html @@ -0,0 +1,14 @@ + + + {{ title }} + + + + + + + + {{ title }} + diff --git a/src/core/viewer/pages/image/image.module.ts b/src/core/viewer/pages/image/image.module.ts new file mode 100644 index 000000000..62cd6dff0 --- /dev/null +++ b/src/core/viewer/pages/image/image.module.ts @@ -0,0 +1,31 @@ +// (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 { NgModule } from '@angular/core'; +import { IonicPageModule } from 'ionic-angular'; +import { TranslateModule } from '@ngx-translate/core'; +import { CoreViewerImagePage } from './image'; +import { CoreDirectivesModule } from '../../../../directives/directives.module'; + +@NgModule({ + declarations: [ + CoreViewerImagePage + ], + imports: [ + CoreDirectivesModule, + IonicPageModule.forChild(CoreViewerImagePage), + TranslateModule.forChild() + ] +}) +export class CoreViewerImagePageModule {} diff --git a/src/core/viewer/pages/image/image.ts b/src/core/viewer/pages/image/image.ts new file mode 100644 index 000000000..6253ddbba --- /dev/null +++ b/src/core/viewer/pages/image/image.ts @@ -0,0 +1,48 @@ +// (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 } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { IonicPage, ViewController, NavParams } from 'ionic-angular'; + +/** + * Page to view an image. If opened as a modal, it will have a button to close the modal. + */ +@IonicPage({segment: 'core-viewer-image'}) +@Component({ + selector: 'page-core-viewer-image', + templateUrl: 'image.html', +}) +export class CoreViewerImagePage { + title: string; // Page title. + image: string; // Image URL. + isModal: boolean; // Whether it should be opened in a modal or in a page. + component: string; // Component to use in external-content. + componentId: string|number; // Component ID to use in external-content. + + constructor(private viewCtrl: ViewController, params: NavParams, translate: TranslateService) { + this.title = params.get('title') || translate.instant('core.imageviewer'); + this.image = params.get('image'); + this.isModal = params.get('isModal'); + this.component = params.get('component'); + this.componentId = params.get('componentId'); + } + + /** + * Close modal. + */ + closeModal() : void { + this.viewCtrl.dismiss(); + } +} \ No newline at end of file diff --git a/src/directives/format-text.ts b/src/directives/format-text.ts index 119985bd8..289bfb23e 100644 --- a/src/directives/format-text.ts +++ b/src/directives/format-text.ts @@ -125,15 +125,35 @@ export class CoreFormatTextDirective implements OnChanges { 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')); - - // @todo: Implement image viewer. Maybe we can add the listener here directly? - container.innerHTML += ''; + this.addMagnifyingGlass(container, img); } } + /** + * Add a magnifying glass icon to view an image at full size. + * + * @param {HTMLElement} container The container of the image. + * @param {HTMLElement} img The image. + */ + addMagnifyingGlass(container: HTMLElement, img: HTMLElement) : void { + let imgSrc = this.textUtils.escapeHTML(img.getAttribute('src')), + label = this.textUtils.escapeHTML(this.translate.instant('core.openfullimage')), + anchor = document.createElement('a'); + + anchor.classList.add('core-image-viewer-icon'); + anchor.setAttribute('aria-label', label); + // Add an ion-icon item to apply the right styles, but the ion-icon component won't be executed. + anchor.innerHTML = ''; + + anchor.addEventListener('click', (e: Event) => { + e.preventDefault(); + e.stopPropagation(); + this.domUtils.viewImage(imgSrc, img.getAttribute('alt'), true, this.component, this.componentId); + }); + + container.appendChild(anchor); + } + /** * Finish the rendering, displaying the element again and calling afterRender. */ diff --git a/src/providers/utils/dom.ts b/src/providers/utils/dom.ts index 92b860324..3a3edbbb6 100644 --- a/src/providers/utils/dom.ts +++ b/src/providers/utils/dom.ts @@ -13,7 +13,8 @@ // limitations under the License. import { Injectable } from '@angular/core'; -import { LoadingController, Loading, ToastController, Toast, AlertController, Alert, Platform, Content } from 'ionic-angular'; +import { LoadingController, Loading, ToastController, Toast, AlertController, Alert, Platform, Content, + NavController, ModalController } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; import { CoreTextUtilsProvider } from './text'; import { CoreAppProvider } from '../app'; @@ -33,7 +34,8 @@ export class CoreDomUtilsProvider { constructor(private translate: TranslateService, private loadingCtrl: LoadingController, private toastCtrl: ToastController, private alertCtrl: AlertController, private textUtils: CoreTextUtilsProvider, private appProvider: CoreAppProvider, - private platform: Platform, private configProvider: CoreConfigProvider, private urlUtils: CoreUrlUtilsProvider) {} + private platform: Platform, private configProvider: CoreConfigProvider, private urlUtils: CoreUrlUtilsProvider, + private modalCtrl: ModalController) {} /** * Wraps a message with core-format-text if the message contains HTML tags. @@ -840,6 +842,40 @@ export class CoreDomUtilsProvider { (el.tagName.toLowerCase() == 'input' && this.inputSupportKeyboard.indexOf(el.type) != -1)); } + /** + * View an image in a new page or modal. + * + * @param {string} image URL of the image. + * @param {string} title Title of the page or modal. + * @param {boolean} [isModal] Whether it should be opened in a modal (true) or in a new page (false). + * @param {string} [component] Component to link the image to if needed. + * @param {string|number} [componentId] An ID to use in conjunction with the component. + * @param {NavController} [navCtrl] The NavController instance to use. + */ + viewImage(image: string, title?: string, isModal?: boolean, component?: string, componentId?: string|number, + navCtrl?: NavController) : void { + if (image) { + let params: any = { + title: title, + image: image, + component: component, + componentId: componentId + }; + + if (isModal) { + // Open a modal with the contents. + params.isModal = true; + + let modal = this.modalCtrl.create('CoreViewerImagePage', params); + modal.present(); + } else if (navCtrl) { + // Open a new page with the contents. + navCtrl.push('CoreViewerImagePage', params); + } + } + + } + /** * Wrap an HTMLElement with another element. *