| import { MathCoach } from "./MathCoach";  | 
|   | 
| /** | 
|  * Hilfsfunktionen für Werkzeug-Entwickler. Diese werden nicht durch die `ide-lib.js`  | 
|  * ausgeliefert! | 
|  */ | 
| export namespace Helpers { | 
|   | 
|     /** | 
|      * Bildet die MathCoach API nach, sodass diese auch Offline (ohne IDE)  | 
|      * verfügbar ist. (Siehe auch `createStorageBasedApi`). | 
|      *  | 
|      * **Hinweis**: Wenn die echte MathCoach-API der IDE verfügbar ist, hat der Aufruf  | 
|      * dieser Funktion keinen Seiteneffekt. | 
|      *  | 
|      * Anwendungsbeispiel  | 
|      *  | 
|      *      import { Helpers } from "@mathcoach/ide-api"; | 
|      *      Helpers.enableOfflineUsageIfNecessary(); | 
|      *      MC.isReady() // use the api | 
|      *  | 
|      * @param contextFileExtension Datei-Erweiterung der Kontext-Datei (Das Werkzeug soll  | 
|      * jedoch unabhängig von der Endung arbeiten können) | 
|      * @returns Gibt `true` zurück, wenn die Offline-Funktionalität aktiviert  | 
|      * wurde, andernfalls `false` | 
|      */ | 
|     export function enableOfflineUsageIfNecessary(contextFileExtension: string = "dummy.json"): boolean { | 
|         if (typeof MC === "undefined") { | 
|             console.warn("you are offline - offline api is used"); | 
|             const WINDOW = (typeof window === "undefined") ? {} as any : window; | 
|             WINDOW.MC = createStorageBasedApi(contextFileExtension); | 
|             return true; | 
|         } else { | 
|             return false; | 
|         } | 
|     } | 
|   | 
|     /** | 
|      * Implementierung der MathCoach API zu Testzwecken. | 
|      *  | 
|      * Das Dateisystem wird durch einen Storage implementiert. Einige Features der  | 
|      * IDE (beispielsweise der Navigator) sind nicht verfügbar und führen keine Aktionen  | 
|      * durch. Alle Aktionen werden in der Browser-Console geloggt. | 
|      *  | 
|      * Anwendungsbeispiel | 
|      *  | 
|      *      import { Helpers } from "@mathcoach/ide-api"; | 
|      *      const _MC = Helpers.createStorageBasedApi(); | 
|      *      const contextFile = await _MC.ide.getContextFile() // use the api | 
|      *  | 
|      * @param contextFileExtension Die Datei-Erweiterung der Kontext-Datei. Das Werkzeug sollte | 
|      *      unabhängig davon implementiert sein, da der Administrator der MathCoach IDE unter  | 
|      *      Umständen auf eine Alternative ausweichen muss. | 
|      * @param storage Storage-Implementierung, die zum Speichern von Dateien verwendet werden  | 
|      *      soll. Standardmäßig wird der `LocalStorage` des Browsers verwendet  | 
|      *      bzw. falls nicht verfügbar der `InMemoryStorage`. | 
|      *  | 
|      */ | 
|     export function createStorageBasedApi( | 
|         contextFileExtension: string = "dummy.json", | 
|         storage: Storage = (typeof localStorage === "undefined") ? new InMemoryStorage() : localStorage | 
|     ): MathCoach.Api { | 
|         const fileIdentifier = (file: MathCoach.File) => `mock-file://${file.owner}@${file.part}/${file.path}`; | 
|         const traceMethod = (method: string, args?: any[]) => console.log(["[MC MOCK API]", " ", method, "(", (args ? args : [""]).join(","), ")"].join("")); | 
|         const runtimeCheck = (errorMessage: string, isInvalid: (param: any) => boolean) => { | 
|             return (paramName: string, value: any, optional: boolean = false) => { | 
|                 if (value === undefined) { | 
|                     if (optional) { | 
|                         return; | 
|                     } else { | 
|                         throw new Error(`missing parameter '${paramName}'`); | 
|                     } | 
|                 } | 
|                 if (isInvalid(value)) { | 
|                     throw new Error(`parameter '${paramName}' is invalid: ${errorMessage}`); | 
|                 } | 
|             } | 
|         } | 
|         const runtimeCheckString = runtimeCheck("not a string", v => typeof v !== "string"); | 
|         const runtimeCheckBoolean = runtimeCheck("not a boolean", v => typeof v !== "boolean"); | 
|         const runtimeCheckFile = runtimeCheck("not a valid file", v => !isFile(v)); | 
|         const api: MathCoach.Api = { | 
|             ide: { | 
|                 async getContextFile(): Promise<MathCoach.File> { | 
|                     traceMethod("MC.ide.getContextFile"); | 
|                     return { | 
|                         owner: "jdoe", | 
|                         path: `/file.${contextFileExtension}`, | 
|                         part: "vfs" | 
|                     } | 
|                 }, | 
|                 async getUserName(): Promise<string> { | 
|                     traceMethod("MC.ide.getUserName"); | 
|                     return "jdoe" | 
|                 }, | 
|                 fs: { | 
|                     async readFile(file: MathCoach.File) { | 
|                         traceMethod("MC.ide.fs.readFile", [JSON.stringify(file)]); | 
|                         runtimeCheckFile("file", file); | 
|                         return storage.getItem(fileIdentifier(file)) || ""; | 
|                     }, | 
|                     async writeFile(file: MathCoach.File, text: string) { | 
|                         traceMethod("MC.ide.fs.writeFile", [JSON.stringify(file), JSON.stringify(`...${text.length} chars...`)]); | 
|                         runtimeCheckFile("file", file); | 
|                         runtimeCheckString("text", text); | 
|                         return storage.setItem(fileIdentifier(file), text); | 
|                     } | 
|                 }, | 
|                 navigator: { | 
|                     async navigateTo(link: string, forceOpen?: boolean) { | 
|                         traceMethod("MC.ide.navigator.navigateTo", [JSON.stringify(link), JSON.stringify(forceOpen ? true : false)]); | 
|                         runtimeCheckString("link", link); | 
|                         runtimeCheckBoolean("forceOpen", forceOpen, true); | 
|                     }, | 
|                     async navigateToExercise(file: MathCoach.File, forceOpen?: boolean) { | 
|                         traceMethod("MC.ide.navigator.navigateToExercise", [JSON.stringify(file), JSON.stringify(forceOpen ? true : false)]); | 
|                         runtimeCheckFile("file", file); | 
|                         runtimeCheckBoolean("forceOpen", forceOpen, true); | 
|                     } | 
|                 } | 
|             }, | 
|             async isReady() { | 
|                 traceMethod("MC.isReady"); | 
|                 return true; | 
|             } | 
|         }; | 
|         return api; | 
|     } | 
|   | 
|   | 
|     /** | 
|      * Type Guard, der prüft, ob es sich um eine gültige `MathCoach.File`-Referenz handelt. | 
|      *  | 
|      * @param maybeFile Ein beliebiges Objekt, das geprüft werden soll | 
|      */ | 
|     export function isFile(maybeFile: any): maybeFile is MathCoach.File { | 
|         return (maybeFile ? true : false) | 
|             && (maybeFile.owner ? true : false) && (typeof maybeFile.owner === "string") | 
|             && (maybeFile.part ? true : false) && (typeof maybeFile.part === "string") | 
|             && (maybeFile.part === "vfs" || maybeFile.part === "www") | 
|             && (maybeFile.path ? true : false) && (typeof maybeFile.path === "string") | 
|             && (maybeFile.path.indexOf("/") === 0) | 
|     } | 
|   | 
|   | 
|     /** | 
|      * Bildet die Kontext-Datei auf eine Aufgaben-Datei mit selben Namen ab. | 
|      *  | 
|      * Ein Werkzeug speichert sein Datenmodell in der sogenannten Kontext-Datei. Soll  | 
|      * eine MathCoach-Aufgabe generiert werden, ist es sinnvoll, wenn die Aufgaben-Datei | 
|      * den selben Namen wie die Kontext-Datei trägt (natürlich mit anderer Dateiendung). | 
|      *  | 
|      * Beispielsweise wird eine Dateireferenz mit dem Pfad `"/path/to/myExercise.mcq.json"`  | 
|      * auf eine Dateireferenz mit dem Pfad `"/path/to/myExercise.groovy"` abgebildet. | 
|      *  | 
|      * Anwendungsbeispiel | 
|      *  | 
|      *      import { Helpers } from "@mathcoach/ide-api"; | 
|      *      const contextFile: MathCoach.File = await MC.ide.getContextFile(); | 
|      *      const exerciseFile: MathCoach.File = Helpers.contextFileToExerciseFile(contextFile); | 
|      */ | 
|     export function contextFileToExerciseFile(contextFile: MathCoach.File): MathCoach.File { | 
|         if (isFile(contextFile)) { | 
|             let exerciseFile: MathCoach.File = { | 
|                 part: contextFile.part, | 
|                 owner: contextFile.owner, | 
|                 path: contextFile.path.split(".")[0] + ".groovy" | 
|             }; | 
|             return exerciseFile; | 
|         } else { | 
|             throw new Error("no valid file reference given, expected object like {owner:'demo', part:'vfs'|'www', path: '/...'}") | 
|         } | 
|     } | 
|   | 
|     /** | 
|      * Storage auf Basis einer Map. Kann z.B. bei Unit-Tests verwendet | 
|      * werden, beid denen der LocalStorage nicht verfügbar ist. | 
|      */ | 
|     export class InMemoryStorage implements Storage { | 
|   | 
|         private readonly items: Map<string, string> = new Map(); | 
|   | 
|         get length(): number { | 
|             return this.items.size; | 
|         } | 
|         clear(): void { | 
|             this.items.clear(); | 
|         } | 
|         getItem(key: string): string | null { | 
|             const value = this.items.get(key); | 
|             return value ? value : null; | 
|         } | 
|         key(_index: number): string | null { | 
|             throw new Error("InMemoryStorage: key function is not implemented now"); | 
|         } | 
|         removeItem(key: string): void { | 
|             this.items.delete(key); | 
|         } | 
|         setItem(key: string, value: string): void { | 
|             this.items.set(key, value); | 
|         } | 
|   | 
|     } | 
|   | 
| } |