<commit_before>// (C) Copyright 2015 Moodle Pty Ltd.
//
// 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, ViewChild, ElementRef, EventEmitter, OnChanges, SimpleChange, OnDestroy,
} from '@angular/core';
import { SafeResourceUrl } from '@angular/platform-browser';

import { CoreFile } from '@services/file';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreUrlUtils } from '@services/utils/url';
import { CoreIframeUtils } from '@services/utils/iframe';
import { CoreUtils } from '@services/utils/utils';
import { DomSanitizer, Router, StatusBar } from '@singletons';
import { CoreEventObserver, CoreEvents } from '@singletons/events';
import { CoreScreen, CoreScreenOrientation } from '@services/screen';
import { Subscription } from 'rxjs';
import { filter } from 'rxjs/operators';
import { NavigationStart } from '@angular/router';

@Component({
    selector: 'core-iframe',
    templateUrl: 'core-iframe.html',
    styleUrls: ['iframe.scss'],
})
export class CoreIframeComponent implements OnChanges, OnDestroy {

    static loadingTimeout = 15000;

    @ViewChild('iframe') iframe?: ElementRef;
    @Input() src?: string;
    @Input() iframeWidth?: string;
    @Input() iframeHeight?: string;
    @Input() allowFullscreen?: boolean | string;
    @Input() showFullscreenOnToolbar?: boolean | string;
    @Input() autoFullscreenOnRotate?: boolean | string;
    @Output() loaded: EventEmitter<HTMLIFrameElement> = new EventEmitter<HTMLIFrameElement>();

    loading?: boolean;
    safeUrl?: SafeResourceUrl;
    displayHelp = false;
    fullscreen = false;

    initialized = false;

    protected style?: HTMLStyleElement;
    protected orientationObs?: CoreEventObserver;
    protected navSubscription?: Subscription;

    constructor() {
        this.loaded = new EventEmitter<HTMLIFrameElement>();
    }

    /**
     * Init the data.
     */
    protected init(): void {
        if (this.initialized) {
            return;
        }

        const iframe: HTMLIFrameElement | undefined = this.iframe?.nativeElement;
        if (!iframe) {
            return;
        }

        this.initialized = true;

        this.iframeWidth = (this.iframeWidth && CoreDomUtils.formatPixelsSize(this.iframeWidth)) || '100%';
        this.iframeHeight = (this.iframeHeight && CoreDomUtils.formatPixelsSize(this.iframeHeight)) || '100%';
        this.allowFullscreen = CoreUtils.isTrueOrOne(this.allowFullscreen);
        this.showFullscreenOnToolbar = CoreUtils.isTrueOrOne(this.showFullscreenOnToolbar);
        this.autoFullscreenOnRotate = CoreUtils.isTrueOrOne(this.autoFullscreenOnRotate);

        if (this.showFullscreenOnToolbar || this.autoFullscreenOnRotate) {
            // Leave fullscreen when navigating.
            this.navSubscription = Router.events
                .pipe(filter(event => event instanceof NavigationStart))
                .subscribe(async () => {
                    if (this.fullscreen) {
                        this.toggleFullscreen(false);
                    }
                });

            const shadow =
                iframe.closest('.ion-page')?.querySelector('ion-header ion-toolbar')?.shadowRoot;
            if (shadow) {
                this.style = document.createElement('style');
                shadow.appendChild(this.style);
            }

            if (this.autoFullscreenOnRotate) {
                this.toggleFullscreen(CoreScreen.isLandscape);

                this.orientationObs = CoreEvents.on(CoreEvents.ORIENTATION_CHANGE, (data) => {
                    this.toggleFullscreen(data.orientation == CoreScreenOrientation.LANDSCAPE);
                });
            }
        }

        // Show loading only with external URLs.
        this.loading = !this.src || !CoreUrlUtils.isLocalFileUrl(this.src);

        CoreIframeUtils.treatFrame(iframe, false);

        iframe.addEventListener('load', () => {
            this.loading = false;
            this.loaded.emit(iframe); // Notify iframe was loaded.
        });

        iframe.addEventListener('error', () => {
            this.loading = false;
            CoreDomUtils.showErrorModal('core.errorloadingcontent', true);
        });

        if (this.loading) {
            setTimeout(() => {
                this.loading = false;
            }, CoreIframeComponent.loadingTimeout);
        }
    }

    /**
     * Detect changes on input properties.
     */
    async ngOnChanges(changes: {[name: string]: SimpleChange }): Promise<void> {
        if (changes.src) {
            const url = CoreUrlUtils.getYoutubeEmbedUrl(changes.src.currentValue) || changes.src.currentValue;
            this.displayHelp = CoreIframeUtils.shouldDisplayHelpForUrl(url);

            await CoreIframeUtils.fixIframeCookies(url);

            this.safeUrl = DomSanitizer.bypassSecurityTrustResourceUrl(CoreFile.convertFileSrc(url));

            // Now that the URL has been set, initialize the iframe. Wait for the iframe to the added to the DOM.
            setTimeout(() => {
                this.init();
            });
        }
    }

    /**
     * Open help modal for iframes.
     */
    openIframeHelpModal(): void {
        CoreIframeUtils.openIframeHelpModal();
    }<commit_msg>MOBILE-3799 iframe: Fix fullscreen on rotate in new pages<commit_after>// (C) Copyright 2015 Moodle Pty Ltd.
//
// 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, ViewChild, ElementRef, EventEmitter, OnChanges, SimpleChange, OnDestroy,
} from '@angular/core';
import { SafeResourceUrl } from '@angular/platform-browser';

import { CoreFile } from '@services/file';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreUrlUtils } from '@services/utils/url';
import { CoreIframeUtils } from '@services/utils/iframe';
import { CoreUtils } from '@services/utils/utils';
import { DomSanitizer, Router, StatusBar } from '@singletons';
import { CoreEventObserver, CoreEvents } from '@singletons/events';
import { CoreScreen, CoreScreenOrientation } from '@services/screen';
import { Subscription } from 'rxjs';
import { filter } from 'rxjs/operators';
import { NavigationStart } from '@angular/router';

@Component({
    selector: 'core-iframe',
    templateUrl: 'core-iframe.html',
    styleUrls: ['iframe.scss'],
})
export class CoreIframeComponent implements OnChanges, OnDestroy {

    static loadingTimeout = 15000;

    @ViewChild('iframe') iframe?: ElementRef;
    @Input() src?: string;
    @Input() iframeWidth?: string;
    @Input() iframeHeight?: string;
    @Input() allowFullscreen?: boolean | string;
    @Input() showFullscreenOnToolbar?: boolean | string;
    @Input() autoFullscreenOnRotate?: boolean | string;
    @Output() loaded: EventEmitter<HTMLIFrameElement> = new EventEmitter<HTMLIFrameElement>();

    loading?: boolean;
    safeUrl?: SafeResourceUrl;
    displayHelp = false;
    fullscreen = false;

    initialized = false;

    protected style?: HTMLStyleElement;
    protected orientationObs?: CoreEventObserver;
    protected navSubscription?: Subscription;

    constructor(protected elementRef: ElementRef<HTMLElement>) {
        this.loaded = new EventEmitter<HTMLIFrameElement>();
    }

    /**
     * Init the data.
     */
    protected init(): void {
        if (this.initialized) {
            return;
        }

        const iframe: HTMLIFrameElement | undefined = this.iframe?.nativeElement;
        if (!iframe) {
            return;
        }

        this.initialized = true;

        this.iframeWidth = (this.iframeWidth && CoreDomUtils.formatPixelsSize(this.iframeWidth)) || '100%';
        this.iframeHeight = (this.iframeHeight && CoreDomUtils.formatPixelsSize(this.iframeHeight)) || '100%';
        this.allowFullscreen = CoreUtils.isTrueOrOne(this.allowFullscreen);
        this.showFullscreenOnToolbar = CoreUtils.isTrueOrOne(this.showFullscreenOnToolbar);
        this.autoFullscreenOnRotate = CoreUtils.isTrueOrOne(this.autoFullscreenOnRotate);

        if (this.showFullscreenOnToolbar || this.autoFullscreenOnRotate) {
            // Leave fullscreen when navigating.
            this.navSubscription = Router.events
                .pipe(filter(event => event instanceof NavigationStart))
                .subscribe(async () => {
                    if (this.fullscreen) {
                        this.toggleFullscreen(false);
                    }
                });

            const shadow =
                iframe.closest('.ion-page')?.querySelector('ion-header ion-toolbar')?.shadowRoot;
            if (shadow) {
                this.style = document.createElement('style');
                shadow.appendChild(this.style);
            }

            if (this.autoFullscreenOnRotate) {
                this.toggleFullscreen(CoreScreen.isLandscape);

                this.orientationObs = CoreEvents.on(CoreEvents.ORIENTATION_CHANGE, (data) => {
                    if (this.isInHiddenPage()) {
                        return;
                    }

                    this.toggleFullscreen(data.orientation == CoreScreenOrientation.LANDSCAPE);
                });
            }
        }

        // Show loading only with external URLs.
        this.loading = !this.src || !CoreUrlUtils.isLocalFileUrl(this.src);

        CoreIframeUtils.treatFrame(iframe, false);

        iframe.addEventListener('load', () => {
            this.loading = false;
            this.loaded.emit(iframe); // Notify iframe was loaded.
        });

        iframe.addEventListener('error', () => {
            this.loading = false;
            CoreDomUtils.showErrorModal('core.errorloadingcontent', true);
        });

        if (this.loading) {
            setTimeout(() => {
                this.loading = false;
            }, CoreIframeComponent.loadingTimeout);
        }
    }

    /**
     * Check if the element is in a hidden page.
     *
     * @return Whether the element is in a hidden page.
     */
    protected isInHiddenPage(): boolean {
        // If we can't find the parent ion-page, consider it to be hidden too.
        return !this.elementRef.nativeElement.closest('.ion-page') || !!this.elementRef.nativeElement.closest('.ion-page-hidden');
    }

    /**
     * Detect changes on input properties.
     */
    async ngOnChanges(changes: {[name: string]: SimpleChange }): Promise<void> {
        if (changes.src) {
            const url = CoreUrlUtils.getYoutubeEmbedUrl(changes.src.currentValue) || changes.src.currentValue;
            this.displayHelp = CoreIframeUtils.shouldDisplayHelpForUrl(url);

            await CoreIframeUtils.fixIframeCookies(url);

            this.safeUrl = DomSanitizer.bypassSecurityTrustResourceUrl(CoreFile.convertFileSrc(url));

            // Now that the URL has been set, initialize the iframe. Wait for the iframe to the added to the DOM.
            setTimeout(() => {
                this.init();
            });
        }
    }

    /**
     * Open help modal for iframes.
     */
    openIframeHelpModal(): void {
        CoreIframeUtils.openIframeHelpModal();
    }
{'fraction_non_alphanumeric': 0.11306075153120344, 'fraction_numerical': 0.0034762456546929316, 'mean_word_length': 2.5907875185735514, 'pattern_counts': {'":': 0, '<': 12, '<?xml version=': 0, '>': 26, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '913752', 'n_tokens_mistral': 3337, 'n_tokens_neox': 3162, 'n_words': 784}