2017-09-26 351 views
7

我需要創建一個窗口對象,以便在iframe中加載的外部文件可以調用nativescript函數。nativescript中的窗口對象

我特別需要窗口對象,因爲我正在加載的文件可以是遵循與LMS相關的符合性(SCORM)的任何文件。

感謝

編輯: 我已經加載的文件是SCORM符合的文件。他們搜索window.API對象/ window.parent.API等,以開始與其中已加載的容器的通信。我無法修改該文件。

如果需要更多的細節,告訴我。

+0

請評論爲什麼downvote? – Deepika

+0

瀏覽器中的'window'是全局JavaScript對象的命名方式,它暴露了某些對象和函數,這些對象和函數只在瀏覽器和其他地方纔有效。 nativescript中沒有'window',也沒有iframe。如果你想模擬某些功能,創建一個'global.window'屬性是完全正確的。另外,對你想要達到的目標的更具體的描述將使我們能夠指導你在哪裏尋找。 – pkanev

+0

謝謝@pkanev,你能分享鏈接,以便我可以參考它。 – Deepika

回答

0

我們在我們的NativeScript應用程序中成功地處理SCORM內容,但它確實需要一點點瑕疵才能完成。

我創建了一個實用程序類,它將向SCORM內容的主入口文件(索引)注入窗口API的覆蓋。

背景:

  • 在我們的應用程序,我們解壓ZIP課程的設備:根據需要爲實現
  • 文件/場/ UUID(這就是身份的引用)
  • 您可以更改路徑工具類的

實例:

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; 
    }); 
} 

實用類:

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

嗨@Sean你如何溝通數據,就像說LMSGetValue被觸發一樣。 – Deepika

+0

WKWebView實現允許偵聽幀中發出的消息:https://github.com/triniwiz/nativescript-webkit-webview/blob/master/src/webkit-webview.ios.ts#L384。我們個人還沒有涉足這些,但我爲未來的實現添加了這些postMessage掛鉤。 –

+0

我已經安裝了market.nativescript.org/plugins/nativescript-webview-interfa ce插件,它可以幫助我溝通並從本地腳本端返回值。 – Deepika