Object window in nativescript

I need to create a window object so that the external file that is loaded inside the iframe can call the nativescript function.

I, in particular, need a window object because the download file can be any file that conforms to the SCORM standard associated with LMS.

Edit : The file I downloaded is a SCORM-compliant file. They are looking for a window.API / window.parent.API object, etc. To start communication with the container in which they were loaded. I can not modify this file.

Tell me if you need more details.

+7
nativescript iframe
source share
1 answer

We successfully process SCORM content in our NativeScript application, but this requires a bit of hacking.

I created a utility class that will override the window API in the main file (index) of the SCORM content.

Background:

  • In our application, we unpack zip courses to devices: documents / courses / UUID (what the person refers to)
  • You can change the path required for your implementation.

An example of using the utility class:

const documents = fs.knownFolders.documents(); const destination = fs.path.join(documents.path, 'courses', this.media.identity); this._readAccessUrl = destination; const src = `file://${destination}`; if (fs.File.exists(destination)) { SCORMUtils.readSCORMManifest(this.media.identity).then(fileName => { this._src = `${src}/${fileName}`; SCORMUtils.makeOfflineCompatible(fs.path.join(destination, fileName)) .then(() => { this._loading = false; }); this._loading = false; }); } 

Utility Class:

 import { File, knownFolders } from 'tns-core-modules/file-system'; const SCORM_API = ` <script type="text/javascript"> (function () { window.parent.API = (function() { return { LMSInitialize: function () { if (window && window.webkit) { window.webkit.messageHandlers.print.postMessage("LMSInitialize"); } return "true"; }, LMSCommit: function () { if (window && window.webkit) { window.webkit.messageHandlers.print.postMessage("LMSCommit"); } return "true"; }, LMSFinish: function () { if (window && window.webkit) { window.webkit.messageHandlers.print.postMessage("LMSFinish"); } return "true"; }, LMSGetValue: function (key) { if (window && window.webkit) { window.webkit.messageHandlers.print.postMessage("LMSGetValue"); } return ""; }, LMSSetValue: function (key, value) { if (window && window.webkit) { window.webkit.messageHandlers.print.postMessage("LMSSetValue"); } return "true"; }, LMSGetLastError: function () { if (window && window.webkit) { window.webkit.messageHandlers.print.postMessage("LMSGetLastError"); } return "0"; }, LMSGetErrorString: function (errorCode) { if (window && window.webkit) { window.webkit.messageHandlers.print.postMessage("LMSGetErrorString"); } return "No error"; }, LMSGetDiagnostic: function (errorCode) { if (window && window.webkit) { window.webkit.messageHandlers.print.postMessage("LMSGetDiagnostic"); } return "No error"; } } })(); })(); </script> </head> `; export class SCORMUtils { /** * Converts a SCORM course to be opened offline * @param entryFile The main entry point determined by imsmanifest.xml */ static makeOfflineCompatible(entryFile: string) { return new Promise((resolve, reject) => { // Rewrite the entry file first this._rewriteFile(entryFile) .then(() => { this._discoverHTMLEntry(entryFile) .then(() => { resolve(); }, () => { console.error('Unable to rewrite alternative HTML entry'); reject(); }); }, () => { console.error(`Unable to rewrite primary entry point: ${entryFile}`); reject(); }); }); } /** * Digests a SCORM Manifest file to determine the main point of entry * for the course viewer. Normally this is a index.html file. */ static readSCORMManifest(identity: string): Promise<string> { return new Promise((resolve, reject) => { const manifestFile = knownFolders.documents() .getFolder('courses') .getFolder(identity) .getFile('imsmanifest.xml'); if (!File.exists(manifestFile.path)) { alert({ title: 'Error', message: 'Course is missing imsmanifest.xml file', okButtonText: 'Ok' }); return reject(); } const data = manifestFile.readTextSync(() => { alert({ title: 'Error', message: 'Cannot open course.', okButtonText: 'Ok' }); return reject(); }); const matches = data.match(/type="webcontent"+.+?href="(.*?)"/); if (matches === null || matches.length < 1) { alert({ title: 'Error', message: 'Invalid imsmanifest.xml file', okButtonText: 'Ok' }); } else { resolve(matches[1]); } }); } /** * Rewrites a file to be SCORM offline-compliant * @param path The path of the file to re-write */ private static _rewriteFile(path: string) { return new Promise((resolve, reject) => { const entryFile = File.fromPath(path); entryFile.readText() .then(htmlText => { this._injectOfflineAPI(htmlText) .then(updatedHtml => { entryFile.writeText(updatedHtml).then(() => { resolve(); }, () => { console.error(`Error writing to file: ${path}`); reject(); }); }); }, () => { console.error(`There was an entry reading the entry file at: ${path}`); reject(); }); }); } /** * Attempts to find another SCORM entry point for re-write * @param mainEntry The main entry point to branch from */ private static _discoverHTMLEntry(mainEntry: string): Promise<any> { return new Promise((resolve, reject) => { const entryFile = File.fromPath(mainEntry); entryFile.readText() .then(htmlText => { let htmlEntry = htmlText.match(/{"type":"html5","url":"(.*?)"}/); if (htmlEntry === null || htmlEntry.length < 1) { // Check for Articulate htmlEntry = htmlText.match(/location\.href\.replace\("index_lms", "(.*?)"/); } if (htmlEntry !== null && htmlEntry.length > 0) { let fileName = htmlEntry[1]; if (fileName.indexOf('.html') === -1) { fileName = `${fileName}.html`; } const directory = mainEntry.substr(0, mainEntry.lastIndexOf('/')); const entryPoint = `${directory}/${fileName}`; if (File.exists(entryPoint)) { this._rewriteFile(entryPoint) .then(() => { resolve(); }, () => { console.error('Error discovering main entry point.'); reject(); }); } else { console.error(`Cannot find alternative entry point: ${entryPoint}`); reject(); } } else { // This course does not have an alternative entry point console.error('Course does not have an alternative entry, skipping...'); resolve(); } }, () => { reject(); }); }); } /** * Injects the extended SCORM API for offline-compatible viewing * @param text The unmodified HTML source text */ private static _injectOfflineAPI(text: string): Promise<string> { return new Promise((resolve, reject) => { // Prevent multiple rewrites of the same file if (this._isConverted(text)) { return resolve(text); } // Finds the end of the head tag for script injection const head = text.match(/<\/head>/gi); if (head !== null && head.length > 0) { resolve(text.replace(head.toString(), SCORM_API)); } else { console.error('Unable to parse incoming HTML for head tag.'); reject({ message: 'Unable to parse HTML' }); } }); } /** * Checks if the HTML has already been converted for offline-viewing * @param text The incoming HTML source text */ private static _isConverted(text: string) { const match = text.match(/window.parent.API/); return match !== null && match.length > 0; } } 
0
source share

All Articles