From 88323319bc7287469c4c395b7fac441a31ee554c Mon Sep 17 00:00:00 2001
From: Dani Palou <dani@moodle.com>
Date: Fri, 20 Sep 2019 11:18:36 +0200
Subject: [PATCH] MOBILE-2491 filter: Implement basic filters

---
 .../activitynames/activitynames.module.ts     |  34 +++++
 .../filter/activitynames/providers/handler.ts |  32 ++++
 src/addon/filter/censor/censor.module.ts      |  34 +++++
 src/addon/filter/censor/providers/handler.ts  |  32 ++++
 src/addon/filter/data/data.module.ts          |  34 +++++
 src/addon/filter/data/providers/handler.ts    |  32 ++++
 .../emailprotect/emailprotect.module.ts       |  34 +++++
 .../filter/emailprotect/providers/handler.ts  |  32 ++++
 src/addon/filter/emoticon/emoticon.module.ts  |  34 +++++
 .../filter/emoticon/providers/handler.ts      |  32 ++++
 src/addon/filter/filter.module.ts             |  47 ++++++
 src/addon/filter/glossary/glossary.module.ts  |  34 +++++
 .../filter/glossary/providers/handler.ts      |  32 ++++
 .../filter/mediaplugin/mediaplugin.module.ts  |  34 +++++
 .../filter/mediaplugin/providers/handler.ts   | 137 ++++++++++++++++++
 .../filter/multilang/multilang.module.ts      |  34 +++++
 .../filter/multilang/providers/handler.ts     |  81 +++++++++++
 src/addon/filter/tex/providers/handler.ts     |  32 ++++
 src/addon/filter/tex/tex.module.ts            |  34 +++++
 src/addon/filter/tidy/providers/handler.ts    |  32 ++++
 src/addon/filter/tidy/tidy.module.ts          |  34 +++++
 .../filter/urltolink/providers/handler.ts     |  32 ++++
 .../filter/urltolink/urltolink.module.ts      |  34 +++++
 src/app/app.module.ts                         |   4 +-
 src/core/filter/providers/delegate.ts         |   4 +-
 src/directives/format-text.ts                 |  85 -----------
 src/providers/utils/text.ts                   |   1 +
 27 files changed, 933 insertions(+), 88 deletions(-)
 create mode 100644 src/addon/filter/activitynames/activitynames.module.ts
 create mode 100644 src/addon/filter/activitynames/providers/handler.ts
 create mode 100644 src/addon/filter/censor/censor.module.ts
 create mode 100644 src/addon/filter/censor/providers/handler.ts
 create mode 100644 src/addon/filter/data/data.module.ts
 create mode 100644 src/addon/filter/data/providers/handler.ts
 create mode 100644 src/addon/filter/emailprotect/emailprotect.module.ts
 create mode 100644 src/addon/filter/emailprotect/providers/handler.ts
 create mode 100644 src/addon/filter/emoticon/emoticon.module.ts
 create mode 100644 src/addon/filter/emoticon/providers/handler.ts
 create mode 100644 src/addon/filter/filter.module.ts
 create mode 100644 src/addon/filter/glossary/glossary.module.ts
 create mode 100644 src/addon/filter/glossary/providers/handler.ts
 create mode 100644 src/addon/filter/mediaplugin/mediaplugin.module.ts
 create mode 100644 src/addon/filter/mediaplugin/providers/handler.ts
 create mode 100644 src/addon/filter/multilang/multilang.module.ts
 create mode 100644 src/addon/filter/multilang/providers/handler.ts
 create mode 100644 src/addon/filter/tex/providers/handler.ts
 create mode 100644 src/addon/filter/tex/tex.module.ts
 create mode 100644 src/addon/filter/tidy/providers/handler.ts
 create mode 100644 src/addon/filter/tidy/tidy.module.ts
 create mode 100644 src/addon/filter/urltolink/providers/handler.ts
 create mode 100644 src/addon/filter/urltolink/urltolink.module.ts

diff --git a/src/addon/filter/activitynames/activitynames.module.ts b/src/addon/filter/activitynames/activitynames.module.ts
new file mode 100644
index 000000000..b3e5d5da7
--- /dev/null
+++ b/src/addon/filter/activitynames/activitynames.module.ts
@@ -0,0 +1,34 @@
+// (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 { IonicModule } from 'ionic-angular';
+import { CoreFilterDelegate } from '@core/filter/providers/delegate';
+import { AddonFilterActivityNamesHandler } from './providers/handler';
+
+@NgModule({
+    declarations: [
+    ],
+    imports: [
+        IonicModule
+    ],
+    providers: [
+        AddonFilterActivityNamesHandler
+    ]
+})
+export class AddonFilterActivityNamesModule {
+    constructor(filterDelegate: CoreFilterDelegate, handler: AddonFilterActivityNamesHandler) {
+        filterDelegate.registerHandler(handler);
+    }
+}
diff --git a/src/addon/filter/activitynames/providers/handler.ts b/src/addon/filter/activitynames/providers/handler.ts
new file mode 100644
index 000000000..8e55846bb
--- /dev/null
+++ b/src/addon/filter/activitynames/providers/handler.ts
@@ -0,0 +1,32 @@
+
+// (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 { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter';
+
+/**
+ * Handler to support the Activity names filter.
+ */
+@Injectable()
+export class AddonFilterActivityNamesHandler extends CoreFilterDefaultHandler {
+    name = 'AddonFilterActivityNamesHandler';
+    filterName = 'activitynames';
+
+    constructor() {
+        super();
+
+        // This filter is handled by Moodle, nothing to do in the app.
+    }
+}
diff --git a/src/addon/filter/censor/censor.module.ts b/src/addon/filter/censor/censor.module.ts
new file mode 100644
index 000000000..126a7bfb2
--- /dev/null
+++ b/src/addon/filter/censor/censor.module.ts
@@ -0,0 +1,34 @@
+// (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 { IonicModule } from 'ionic-angular';
+import { CoreFilterDelegate } from '@core/filter/providers/delegate';
+import { AddonFilterCensorHandler } from './providers/handler';
+
+@NgModule({
+    declarations: [
+    ],
+    imports: [
+        IonicModule
+    ],
+    providers: [
+        AddonFilterCensorHandler
+    ]
+})
+export class AddonFilterCensorModule {
+    constructor(filterDelegate: CoreFilterDelegate, handler: AddonFilterCensorHandler) {
+        filterDelegate.registerHandler(handler);
+    }
+}
diff --git a/src/addon/filter/censor/providers/handler.ts b/src/addon/filter/censor/providers/handler.ts
new file mode 100644
index 000000000..874d86ef9
--- /dev/null
+++ b/src/addon/filter/censor/providers/handler.ts
@@ -0,0 +1,32 @@
+
+// (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 { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter';
+
+/**
+ * Handler to support the Word censorship filter.
+ */
+@Injectable()
+export class AddonFilterCensorHandler extends CoreFilterDefaultHandler {
+    name = 'AddonFilterCensorHandler';
+    filterName = 'censor';
+
+    constructor() {
+        super();
+
+        // This filter is handled by Moodle, nothing to do in the app.
+    }
+}
diff --git a/src/addon/filter/data/data.module.ts b/src/addon/filter/data/data.module.ts
new file mode 100644
index 000000000..1272618d8
--- /dev/null
+++ b/src/addon/filter/data/data.module.ts
@@ -0,0 +1,34 @@
+// (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 { IonicModule } from 'ionic-angular';
+import { CoreFilterDelegate } from '@core/filter/providers/delegate';
+import { AddonFilterDataHandler } from './providers/handler';
+
+@NgModule({
+    declarations: [
+    ],
+    imports: [
+        IonicModule
+    ],
+    providers: [
+        AddonFilterDataHandler
+    ]
+})
+export class AddonFilterDataModule {
+    constructor(filterDelegate: CoreFilterDelegate, handler: AddonFilterDataHandler) {
+        filterDelegate.registerHandler(handler);
+    }
+}
diff --git a/src/addon/filter/data/providers/handler.ts b/src/addon/filter/data/providers/handler.ts
new file mode 100644
index 000000000..f01cadb1d
--- /dev/null
+++ b/src/addon/filter/data/providers/handler.ts
@@ -0,0 +1,32 @@
+
+// (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 { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter';
+
+/**
+ * Handler to support the Database auto-link filter.
+ */
+@Injectable()
+export class AddonFilterDataHandler extends CoreFilterDefaultHandler {
+    name = 'AddonFilterDataHandler';
+    filterName = 'data';
+
+    constructor() {
+        super();
+
+        // This filter is handled by Moodle, nothing to do in the app.
+    }
+}
diff --git a/src/addon/filter/emailprotect/emailprotect.module.ts b/src/addon/filter/emailprotect/emailprotect.module.ts
new file mode 100644
index 000000000..d746dfc37
--- /dev/null
+++ b/src/addon/filter/emailprotect/emailprotect.module.ts
@@ -0,0 +1,34 @@
+// (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 { IonicModule } from 'ionic-angular';
+import { CoreFilterDelegate } from '@core/filter/providers/delegate';
+import { AddonFilterEmailProtectHandler } from './providers/handler';
+
+@NgModule({
+    declarations: [
+    ],
+    imports: [
+        IonicModule
+    ],
+    providers: [
+        AddonFilterEmailProtectHandler
+    ]
+})
+export class AddonFilterEmailProtectModule {
+    constructor(filterDelegate: CoreFilterDelegate, handler: AddonFilterEmailProtectHandler) {
+        filterDelegate.registerHandler(handler);
+    }
+}
diff --git a/src/addon/filter/emailprotect/providers/handler.ts b/src/addon/filter/emailprotect/providers/handler.ts
new file mode 100644
index 000000000..e7971386c
--- /dev/null
+++ b/src/addon/filter/emailprotect/providers/handler.ts
@@ -0,0 +1,32 @@
+
+// (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 { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter';
+
+/**
+ * Handler to support the Email protection filter.
+ */
+@Injectable()
+export class AddonFilterEmailProtectHandler extends CoreFilterDefaultHandler {
+    name = 'AddonFilterEmailProtectHandler';
+    filterName = 'emailprotect';
+
+    constructor() {
+        super();
+
+        // This filter is handled by Moodle, nothing to do in the app.
+    }
+}
diff --git a/src/addon/filter/emoticon/emoticon.module.ts b/src/addon/filter/emoticon/emoticon.module.ts
new file mode 100644
index 000000000..5237286f9
--- /dev/null
+++ b/src/addon/filter/emoticon/emoticon.module.ts
@@ -0,0 +1,34 @@
+// (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 { IonicModule } from 'ionic-angular';
+import { CoreFilterDelegate } from '@core/filter/providers/delegate';
+import { AddonFilterEmoticonHandler } from './providers/handler';
+
+@NgModule({
+    declarations: [
+    ],
+    imports: [
+        IonicModule
+    ],
+    providers: [
+        AddonFilterEmoticonHandler
+    ]
+})
+export class AddonFilterEmoticonModule {
+    constructor(filterDelegate: CoreFilterDelegate, handler: AddonFilterEmoticonHandler) {
+        filterDelegate.registerHandler(handler);
+    }
+}
diff --git a/src/addon/filter/emoticon/providers/handler.ts b/src/addon/filter/emoticon/providers/handler.ts
new file mode 100644
index 000000000..39c2ffe33
--- /dev/null
+++ b/src/addon/filter/emoticon/providers/handler.ts
@@ -0,0 +1,32 @@
+
+// (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 { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter';
+
+/**
+ * Handler to support the Emoticon filter.
+ */
+@Injectable()
+export class AddonFilterEmoticonHandler extends CoreFilterDefaultHandler {
+    name = 'AddonFilterEmoticonHandler';
+    filterName = 'emoticon';
+
+    constructor() {
+        super();
+
+        // This filter is handled by Moodle, nothing to do in the app.
+    }
+}
diff --git a/src/addon/filter/filter.module.ts b/src/addon/filter/filter.module.ts
new file mode 100644
index 000000000..b20447e06
--- /dev/null
+++ b/src/addon/filter/filter.module.ts
@@ -0,0 +1,47 @@
+// (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 { AddonFilterActivityNamesModule } from './activitynames/activitynames.module';
+import { AddonFilterCensorModule } from './censor/censor.module';
+import { AddonFilterDataModule } from './data/data.module';
+import { AddonFilterEmailProtectModule } from './emailprotect/emailprotect.module';
+import { AddonFilterEmoticonModule } from './emoticon/emoticon.module';
+import { AddonFilterGlossaryModule } from './glossary/glossary.module';
+import { AddonFilterMediaPluginModule } from './mediaplugin/mediaplugin.module';
+import { AddonFilterMultilangModule } from './multilang/multilang.module';
+import { AddonFilterTexModule } from './tex/tex.module';
+import { AddonFilterTidyModule } from './tidy/tidy.module';
+import { AddonFilterUrlToLinkModule } from './urltolink/urltolink.module';
+
+@NgModule({
+    declarations: [],
+    imports: [
+        AddonFilterActivityNamesModule,
+        AddonFilterCensorModule,
+        AddonFilterDataModule,
+        AddonFilterEmailProtectModule,
+        AddonFilterEmoticonModule,
+        AddonFilterGlossaryModule,
+        AddonFilterMediaPluginModule,
+        AddonFilterMultilangModule,
+        AddonFilterTexModule,
+        AddonFilterTidyModule,
+        AddonFilterUrlToLinkModule
+    ],
+    providers: [
+    ],
+    exports: []
+})
+export class AddonFilterModule { }
diff --git a/src/addon/filter/glossary/glossary.module.ts b/src/addon/filter/glossary/glossary.module.ts
new file mode 100644
index 000000000..337bffbbc
--- /dev/null
+++ b/src/addon/filter/glossary/glossary.module.ts
@@ -0,0 +1,34 @@
+// (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 { IonicModule } from 'ionic-angular';
+import { CoreFilterDelegate } from '@core/filter/providers/delegate';
+import { AddonFilterGlossaryHandler } from './providers/handler';
+
+@NgModule({
+    declarations: [
+    ],
+    imports: [
+        IonicModule
+    ],
+    providers: [
+        AddonFilterGlossaryHandler
+    ]
+})
+export class AddonFilterGlossaryModule {
+    constructor(filterDelegate: CoreFilterDelegate, handler: AddonFilterGlossaryHandler) {
+        filterDelegate.registerHandler(handler);
+    }
+}
diff --git a/src/addon/filter/glossary/providers/handler.ts b/src/addon/filter/glossary/providers/handler.ts
new file mode 100644
index 000000000..f7e38d553
--- /dev/null
+++ b/src/addon/filter/glossary/providers/handler.ts
@@ -0,0 +1,32 @@
+
+// (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 { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter';
+
+/**
+ * Handler to support the Glossary auto-link filter.
+ */
+@Injectable()
+export class AddonFilterGlossaryHandler extends CoreFilterDefaultHandler {
+    name = 'AddonFilterGlossaryHandler';
+    filterName = 'glossary';
+
+    constructor() {
+        super();
+
+        // This filter is handled by Moodle, nothing to do in the app.
+    }
+}
diff --git a/src/addon/filter/mediaplugin/mediaplugin.module.ts b/src/addon/filter/mediaplugin/mediaplugin.module.ts
new file mode 100644
index 000000000..2ef135ddd
--- /dev/null
+++ b/src/addon/filter/mediaplugin/mediaplugin.module.ts
@@ -0,0 +1,34 @@
+// (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 { IonicModule } from 'ionic-angular';
+import { CoreFilterDelegate } from '@core/filter/providers/delegate';
+import { AddonFilterMediaPluginHandler } from './providers/handler';
+
+@NgModule({
+    declarations: [
+    ],
+    imports: [
+        IonicModule
+    ],
+    providers: [
+        AddonFilterMediaPluginHandler
+    ]
+})
+export class AddonFilterMediaPluginModule {
+    constructor(filterDelegate: CoreFilterDelegate, handler: AddonFilterMediaPluginHandler) {
+        filterDelegate.registerHandler(handler);
+    }
+}
diff --git a/src/addon/filter/mediaplugin/providers/handler.ts b/src/addon/filter/mediaplugin/providers/handler.ts
new file mode 100644
index 000000000..29da3de94
--- /dev/null
+++ b/src/addon/filter/mediaplugin/providers/handler.ts
@@ -0,0 +1,137 @@
+
+// (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 { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter';
+import { CoreFilterFilter } from '@core/filter/providers/filter';
+import { CoreTextUtilsProvider } from '@providers/utils/text';
+
+/**
+ * Handler to support the Multimedia filter.
+ */
+@Injectable()
+export class AddonFilterMediaPluginHandler extends CoreFilterDefaultHandler {
+    name = 'AddonFilterMediaPluginHandler';
+    filterName = 'mediaplugin';
+
+    constructor(private textUtils: CoreTextUtilsProvider) {
+        super();
+    }
+
+    /**
+     * Filter some text.
+     *
+     * @param text The text to filter.
+     * @param filter The filter.
+     * @param options Options passed to the filters.
+     * @return Filtered text (or promise resolved with the filtered text).
+     */
+    filter(text: string, filter: CoreFilterFilter, options: any): string | Promise<string> {
+
+        const div = document.createElement('div');
+        div.innerHTML = text;
+
+        const videos = Array.from(div.querySelectorAll('video'));
+
+        videos.forEach((video) => {
+            this.treatVideoFilters(video);
+        });
+
+        return div.innerHTML;
+    }
+
+    /**
+     * Treat video filters. Currently only treating youtube video using video JS.
+     *
+     * @param el Video element.
+     * @param navCtrl NavController to use.
+     */
+    protected treatVideoFilters(video: HTMLElement): void {
+        // Treat Video JS Youtube video links and translate them to iframes.
+        if (!video.classList.contains('video-js')) {
+            return;
+        }
+
+        const data = this.textUtils.parseJSON(video.getAttribute('data-setup') || video.getAttribute('data-setup-lazy') || '{}'),
+            youtubeData = data.techOrder && data.techOrder[0] && data.techOrder[0] == 'youtube' &&
+                    this.parseYoutubeUrl(data.sources && data.sources[0] && data.sources[0].src);
+
+        if (!youtubeData || !youtubeData.videoId) {
+            return;
+        }
+
+        const iframe = document.createElement('iframe');
+        iframe.id = video.id;
+        iframe.src = 'https://www.youtube.com/embed/' + youtubeData.videoId; // Don't apply other params to align with Moodle web.
+        iframe.setAttribute('frameborder', '0');
+        iframe.setAttribute('allowfullscreen', '1');
+        iframe.width = '100%';
+        iframe.height = '300';
+
+        // Replace video tag by the iframe.
+        video.parentNode.replaceChild(iframe, video);
+    }
+
+    /**
+     * Parse a YouTube URL.
+     * Based on Youtube.parseUrl from Moodle media/player/videojs/amd/src/Youtube-lazy.js
+     *
+     * @param url URL of the video.
+     * @return Data of the video.
+     */
+    protected parseYoutubeUrl(url: string): {videoId: string, listId?: string, start?: number} {
+        const result = {
+            videoId: null,
+            listId: null,
+            start: null
+        };
+
+        if (!url) {
+            return result;
+        }
+
+        url = this.textUtils.decodeHTML(url);
+
+        // Get the video ID.
+        let match = url.match(/^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/);
+
+        if (match && match[2].length === 11) {
+            result.videoId = match[2];
+        }
+
+        // Now get the playlist (if any).
+        match = url.match(/[?&]list=([^#\&\?]+)/);
+
+        if (match && match[1]) {
+            result.listId = match[1];
+        }
+
+        // Now get the start time (if any).
+        match = url.match(/[?&]start=(\d+)/);
+
+        if (match && match[1]) {
+            result.start = parseInt(match[1], 10);
+        } else {
+            // No start param, but it could have a time param.
+            match = url.match(/[?&]t=(\d+h)?(\d+m)?(\d+s)?/);
+            if (match) {
+                result.start = (match[1] ? parseInt(match[1], 10) * 3600 : 0) + (match[2] ? parseInt(match[2], 10) * 60 : 0) +
+                        (match[3] ? parseInt(match[3], 10) : 0);
+            }
+        }
+
+        return result;
+    }
+}
diff --git a/src/addon/filter/multilang/multilang.module.ts b/src/addon/filter/multilang/multilang.module.ts
new file mode 100644
index 000000000..149cdfd0c
--- /dev/null
+++ b/src/addon/filter/multilang/multilang.module.ts
@@ -0,0 +1,34 @@
+// (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 { IonicModule } from 'ionic-angular';
+import { CoreFilterDelegate } from '@core/filter/providers/delegate';
+import { AddonFilterMultilangHandler } from './providers/handler';
+
+@NgModule({
+    declarations: [
+    ],
+    imports: [
+        IonicModule
+    ],
+    providers: [
+        AddonFilterMultilangHandler
+    ]
+})
+export class AddonFilterMultilangModule {
+    constructor(filterDelegate: CoreFilterDelegate, handler: AddonFilterMultilangHandler) {
+        filterDelegate.registerHandler(handler);
+    }
+}
diff --git a/src/addon/filter/multilang/providers/handler.ts b/src/addon/filter/multilang/providers/handler.ts
new file mode 100644
index 000000000..e689d9769
--- /dev/null
+++ b/src/addon/filter/multilang/providers/handler.ts
@@ -0,0 +1,81 @@
+
+// (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 { CoreSitesProvider } from '@providers/sites';
+import { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter';
+import { CoreFilterFilter } from '@core/filter/providers/filter';
+import { CoreLangProvider } from '@providers/lang';
+
+/**
+ * Handler to support the Multilang filter.
+ */
+@Injectable()
+export class AddonFilterMultilangHandler extends CoreFilterDefaultHandler {
+    name = 'AddonFilterMultilangHandler';
+    filterName = 'multilang';
+
+    constructor(private langProvider: CoreLangProvider,
+            private sitesProvider: CoreSitesProvider) {
+        super();
+    }
+
+    /**
+     * Whether or not the handler is enabled on a site level.
+     *
+     * @return {boolean|Promise<boolean>} Whether or not the handler is enabled on a site level.
+     */
+    isEnabled(): boolean | Promise<boolean> {
+        // In Moodle versions older than 3.7, some specific content can be received unfiltered. Filter it in the app.
+        const currentSite = this.sitesProvider.getCurrentSite();
+
+        return !currentSite.isVersionGreaterEqualThan('3.7');
+    }
+
+    /**
+     * Filter some text.
+     *
+     * @param text The text to filter.
+     * @param filter The filter.
+     * @param options Options passed to the filters.
+     * @return Filtered text (or promise resolved with the filtered text).
+     */
+    filter(text: string, filter: CoreFilterFilter, options: any): string | Promise<string> {
+
+        return this.langProvider.getCurrentLanguage().then((language) => {
+            // Match the current language.
+            const anyLangRegEx = /<(?:lang|span)[^>]+lang="[a-zA-Z0-9_-]+"[^>]*>(.*?)<\/(?:lang|span)>/g;
+            let currentLangRegEx = new RegExp('<(?:lang|span)[^>]+lang="' + language + '"[^>]*>(.*?)<\/(?:lang|span)>', 'g');
+
+            if (!text.match(currentLangRegEx)) {
+                // Current lang not found. Try to find the first language.
+                const matches = text.match(anyLangRegEx);
+                if (matches && matches[0]) {
+                    language = matches[0].match(/lang="([a-zA-Z0-9_-]+)"/)[1];
+                    currentLangRegEx = new RegExp('<(?:lang|span)[^>]+lang="' + language + '"[^>]*>(.*?)<\/(?:lang|span)>', 'g');
+                } else {
+                    // No multi-lang tag found, stop.
+                    return text;
+                }
+            }
+            // Extract contents of current language.
+            text = text.replace(currentLangRegEx, '$1');
+            // Delete the rest of languages
+            text = text.replace(anyLangRegEx, '');
+
+            return text;
+        });
+    }
+}
diff --git a/src/addon/filter/tex/providers/handler.ts b/src/addon/filter/tex/providers/handler.ts
new file mode 100644
index 000000000..a02e8e7c7
--- /dev/null
+++ b/src/addon/filter/tex/providers/handler.ts
@@ -0,0 +1,32 @@
+
+// (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 { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter';
+
+/**
+ * Handler to support the TeX notation filter.
+ */
+@Injectable()
+export class AddonFilterTexHandler extends CoreFilterDefaultHandler {
+    name = 'AddonFilterTexHandler';
+    filterName = 'tex';
+
+    constructor() {
+        super();
+
+        // This filter is handled by Moodle, nothing to do in the app.
+    }
+}
diff --git a/src/addon/filter/tex/tex.module.ts b/src/addon/filter/tex/tex.module.ts
new file mode 100644
index 000000000..21084238b
--- /dev/null
+++ b/src/addon/filter/tex/tex.module.ts
@@ -0,0 +1,34 @@
+// (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 { IonicModule } from 'ionic-angular';
+import { CoreFilterDelegate } from '@core/filter/providers/delegate';
+import { AddonFilterTexHandler } from './providers/handler';
+
+@NgModule({
+    declarations: [
+    ],
+    imports: [
+        IonicModule
+    ],
+    providers: [
+        AddonFilterTexHandler
+    ]
+})
+export class AddonFilterTexModule {
+    constructor(filterDelegate: CoreFilterDelegate, handler: AddonFilterTexHandler) {
+        filterDelegate.registerHandler(handler);
+    }
+}
diff --git a/src/addon/filter/tidy/providers/handler.ts b/src/addon/filter/tidy/providers/handler.ts
new file mode 100644
index 000000000..e0fff232d
--- /dev/null
+++ b/src/addon/filter/tidy/providers/handler.ts
@@ -0,0 +1,32 @@
+
+// (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 { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter';
+
+/**
+ * Handler to support the HTML tidy filter.
+ */
+@Injectable()
+export class AddonFilterTidyHandler extends CoreFilterDefaultHandler {
+    name = 'AddonFilterTidyHandler';
+    filterName = 'tidy';
+
+    constructor() {
+        super();
+
+        // This filter is handled by Moodle, nothing to do in the app.
+    }
+}
diff --git a/src/addon/filter/tidy/tidy.module.ts b/src/addon/filter/tidy/tidy.module.ts
new file mode 100644
index 000000000..ab0b43098
--- /dev/null
+++ b/src/addon/filter/tidy/tidy.module.ts
@@ -0,0 +1,34 @@
+// (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 { IonicModule } from 'ionic-angular';
+import { CoreFilterDelegate } from '@core/filter/providers/delegate';
+import { AddonFilterTidyHandler } from './providers/handler';
+
+@NgModule({
+    declarations: [
+    ],
+    imports: [
+        IonicModule
+    ],
+    providers: [
+        AddonFilterTidyHandler
+    ]
+})
+export class AddonFilterTidyModule {
+    constructor(filterDelegate: CoreFilterDelegate, handler: AddonFilterTidyHandler) {
+        filterDelegate.registerHandler(handler);
+    }
+}
diff --git a/src/addon/filter/urltolink/providers/handler.ts b/src/addon/filter/urltolink/providers/handler.ts
new file mode 100644
index 000000000..d23c392b3
--- /dev/null
+++ b/src/addon/filter/urltolink/providers/handler.ts
@@ -0,0 +1,32 @@
+
+// (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 { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter';
+
+/**
+ * Handler to support the URL to link and images filter.
+ */
+@Injectable()
+export class AddonFilterUrlToLinkHandler extends CoreFilterDefaultHandler {
+    name = 'AddonFilterUrlToLinkHandler';
+    filterName = 'urltolink';
+
+    constructor() {
+        super();
+
+        // This filter is handled by Moodle, nothing to do in the app.
+    }
+}
diff --git a/src/addon/filter/urltolink/urltolink.module.ts b/src/addon/filter/urltolink/urltolink.module.ts
new file mode 100644
index 000000000..afbdbf15c
--- /dev/null
+++ b/src/addon/filter/urltolink/urltolink.module.ts
@@ -0,0 +1,34 @@
+// (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 { IonicModule } from 'ionic-angular';
+import { CoreFilterDelegate } from '@core/filter/providers/delegate';
+import { AddonFilterUrlToLinkHandler } from './providers/handler';
+
+@NgModule({
+    declarations: [
+    ],
+    imports: [
+        IonicModule
+    ],
+    providers: [
+        AddonFilterUrlToLinkHandler
+    ]
+})
+export class AddonFilterUrlToLinkModule {
+    constructor(filterDelegate: CoreFilterDelegate, handler: AddonFilterUrlToLinkHandler) {
+        filterDelegate.registerHandler(handler);
+    }
+}
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 740759df7..6f9966fa8 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -148,6 +148,7 @@ import { AddonRemoteThemesModule } from '@addon/remotethemes/remotethemes.module
 import { AddonQbehaviourModule } from '@addon/qbehaviour/qbehaviour.module';
 import { AddonQtypeModule } from '@addon/qtype/qtype.module';
 import { AddonStorageManagerModule } from '@addon/storagemanager/storagemanager.module';
+import { AddonFilterModule } from '@addon/filter/filter.module';
 
 // For translate loader. AoT requires an exported function for factories.
 export function createTranslateLoader(http: HttpClient): TranslateHttpLoader {
@@ -290,7 +291,8 @@ export const WP_PROVIDER: any = null;
         AddonRemoteThemesModule,
         AddonQbehaviourModule,
         AddonQtypeModule,
-        AddonStorageManagerModule
+        AddonStorageManagerModule,
+        AddonFilterModule
     ],
     bootstrap: [IonicApp],
     entryComponents: [
diff --git a/src/core/filter/providers/delegate.ts b/src/core/filter/providers/delegate.ts
index 4d5913c6a..4fd742262 100644
--- a/src/core/filter/providers/delegate.ts
+++ b/src/core/filter/providers/delegate.ts
@@ -74,8 +74,8 @@ export class CoreFilterDelegate extends CoreDelegate {
      * @return Promise resolved with the filtered text.
      */
     filterText(text: string, filters: CoreFilterFilter[], options?: any, skipFilters?: string[]): Promise<string> {
-        if (!text) {
-            return Promise.resolve(text);
+        if (!text || typeof text != 'string') {
+            return Promise.resolve('');
         }
 
         // Wait for filters to be initialized.
diff --git a/src/directives/format-text.ts b/src/directives/format-text.ts
index a723f2116..88fb67c20 100644
--- a/src/directives/format-text.ts
+++ b/src/directives/format-text.ts
@@ -425,7 +425,6 @@ export class CoreFormatTextDirective implements OnChanges {
             });
 
             videos.forEach((video) => {
-                this.treatVideoFilters(video, navCtrl);
                 this.treatMedia(video);
             });
 
@@ -549,40 +548,6 @@ export class CoreFormatTextDirective implements OnChanges {
         this.showMoreDisplayed = false;
     }
 
-    /**
-     * Treat video filters. Currently only treating youtube video using video JS.
-     *
-     * @param el Video element.
-     * @param navCtrl NavController to use.
-     */
-    protected treatVideoFilters(video: HTMLElement, navCtrl: NavController): void {
-        // Treat Video JS Youtube video links and translate them to iframes.
-        if (!video.classList.contains('video-js')) {
-            return;
-        }
-
-        const data = this.textUtils.parseJSON(video.getAttribute('data-setup') || video.getAttribute('data-setup-lazy') || '{}'),
-            youtubeData = data.techOrder && data.techOrder[0] && data.techOrder[0] == 'youtube' &&
-                    this.parseYoutubeUrl(data.sources && data.sources[0] && data.sources[0].src);
-
-        if (!youtubeData || !youtubeData.videoId) {
-            return;
-        }
-
-        const iframe = document.createElement('iframe');
-        iframe.id = video.id;
-        iframe.src = 'https://www.youtube.com/embed/' + youtubeData.videoId; // Don't apply other params to align with Moodle web.
-        iframe.setAttribute('frameborder', '0');
-        iframe.setAttribute('allowfullscreen', '1');
-        iframe.width = '100%';
-        iframe.height = '300';
-
-        // Replace video tag by the iframe.
-        video.parentNode.replaceChild(iframe, video);
-
-        this.iframeUtils.treatFrame(iframe, false, navCtrl);
-    }
-
     /**
      * Add media adapt class and apply CoreExternalContentDirective to the media element and its sources and tracks.
      *
@@ -693,54 +658,4 @@ export class CoreFormatTextDirective implements OnChanges {
 
         this.iframeUtils.treatFrame(iframe, false, navCtrl);
     }
-
-    /**
-     * Parse a YouTube URL.
-     * Based on Youtube.parseUrl from Moodle media/player/videojs/amd/src/Youtube-lazy.js
-     *
-     * @param url URL of the video.
-     */
-    protected parseYoutubeUrl(url: string): {videoId: string, listId?: string, start?: number} {
-        const result = {
-            videoId: null,
-            listId: null,
-            start: null
-        };
-
-        if (!url) {
-            return result;
-        }
-
-        url = this.textUtils.decodeHTML(url);
-
-        // Get the video ID.
-        let match = url.match(/^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/);
-
-        if (match && match[2].length === 11) {
-            result.videoId = match[2];
-        }
-
-        // Now get the playlist (if any).
-        match = url.match(/[?&]list=([^#\&\?]+)/);
-
-        if (match && match[1]) {
-            result.listId = match[1];
-        }
-
-        // Now get the start time (if any).
-        match = url.match(/[?&]start=(\d+)/);
-
-        if (match && match[1]) {
-            result.start = parseInt(match[1], 10);
-        } else {
-            // No start param, but it could have a time param.
-            match = url.match(/[?&]t=(\d+h)?(\d+m)?(\d+s)?/);
-            if (match) {
-                result.start = (match[1] ? parseInt(match[1], 10) * 3600 : 0) + (match[2] ? parseInt(match[2], 10) * 60 : 0) +
-                        (match[3] ? parseInt(match[3], 10) : 0);
-            }
-        }
-
-        return result;
-    }
 }
diff --git a/src/providers/utils/text.ts b/src/providers/utils/text.ts
index 9a6b7f005..262662da6 100644
--- a/src/providers/utils/text.ts
+++ b/src/providers/utils/text.ts
@@ -732,6 +732,7 @@ export class CoreTextUtilsProvider {
      *
      * @param text The text to be treated.
      * @return Promise resolved with the formatted text.
+     * @deprecated since 3.8.0. Now this is handled by AddonFilterMultilangHandler.
      */
     treatMultilangTags(text: string): Promise<string> {
         if (!text || typeof text != 'string') {