Nous traitons avec succès le contenu SCORM dans notre application NativeScript, mais cela nécessite un peu de hackery.
J'ai créé une classe d'utilitaires qui injectera dans le fichier d'entrée principal (index) du contenu SCORM un remplacement pour l'API de fenêtre.
Contexte:
- Dans notre application, nous Décompresser les cours postaux aux appareils: documents/cours/UUID (c'est ce que l'identité fait référence)
- Vous pouvez modifier le chemin si nécessaire pour votre mise en œuvre
Exemple d'utilisation de la classe de service:
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;
});
}
Classe utilitaire:
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;
}
}
Veuillez indiquer pourquoi downview? – Deepika
'window' dans les navigateurs est comment l'objet javascript global est nommé, il expose certains objets et fonctions qui ne sont valables que dans le contexte des navigateurs et nulle part ailleurs. Il n'y a pas de "fenêtre" dans nativescript, ni d'iframe. Si vous souhaitez vous moquez de certaines fonctionnalités, la création d'une propriété 'global.window' est totalement satisfaisante. En outre, une description plus précise de ce que vous tentez de réaliser nous permettra de vous donner des indications sur les points à examiner. – pkanev
Merci @pkanev, Pouvez-vous partager le lien afin que je puisse le référer. – Deepika