File size: 5,588 Bytes
7aec436
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import Cocoa
import WebKit

class ViewController: NSViewController, WKNavigationDelegate, WKUIDelegate, WKScriptMessageHandler {
    @IBOutlet var webView: WKWebView!

    var windowTitle: String = "Window Title"

    override func loadView() {
        super.loadView()

        let configUrl = Bundle.main.url(forResource: "application_config", withExtension: "json")!
        let configContents = try! Data(contentsOf: configUrl);
        let configParsed = try! JSONSerialization.jsonObject(with: configContents, options: [])

        var width = 480
        var height = 360
        var background = NSColor.black.cgColor
        if let dict = configParsed as? [String: Any] {
            if let title = dict["title"] as? String {
                self.windowTitle = title
            }
            if let number = dict["width"] as? Int {
                width = number
            }
            if let number = dict["height"] as? Int {
                height = number
            }
            if let color = dict["background"] as? [Int] {
                background = NSColor(
                    red: CGFloat(color[0]) / 255.0,
                    green: CGFloat(color[1]) / 255.0,
                    blue: CGFloat(color[2]) / 255.0,
                    alpha: CGFloat(color[3])
                ).cgColor
            }
        }
        view.wantsLayer = true
        view.layer?.backgroundColor = background
        view.frame = CGRect(x: 0, y: 0, width: width, height: height)

        webView.navigationDelegate = self
        webView.uiDelegate = self
        webView.configuration.userContentController.add(self, name: "download")
        webView.configuration.preferences.setValue(true, forKey: "allowFileAccessFromFileURLs")
        #if DEBUG
        webView.configuration.preferences.setValue(true, forKey: "developerExtrasEnabled")
        #endif
        webView.isHidden = true
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        let url = Bundle.main.url(forResource: "index", withExtension: "html")!
        webView.loadFileURL(url, allowingReadAccessTo: url.deletingLastPathComponent())
    }

    override func viewDidAppear() {
        super.viewDidAppear()
        view.window?.title = windowTitle
    }

    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
//        The packager will use window.ExternalDownloadHelper instead of native JS file downloads.
//        We use this because older versions of macOS's WKWebView do not handle file downloads
//        properly or at all.
        webView.evaluateJavaScript("""

        window.ExternalDownloadHelper = {

            download: (filename, blob) => {

                const reader = new FileReader();

                reader.onload = () => {

                    const array = Array.from(new Uint8Array(reader.result));

                    webkit.messageHandlers.download.postMessage({

                        name: filename,

                        data: array

                    });

                };

                reader.onerror = () => {

                    console.error(reader.error);

                };

                reader.readAsArrayBuffer(blob);

            }

        };

        """)
        webView.isHidden = false
    }

    func webView(_ webView: WKWebView, runOpenPanelWith parameters: WKOpenPanelParameters, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping ([URL]?) -> Void) {
//        Used by list import.
        let panel = NSOpenPanel()
        panel.canChooseFiles = true
        panel.canChooseDirectories = false
        panel.allowsMultipleSelection = parameters.allowsMultipleSelection
        panel.allowedFileTypes = ["txt", "csv", "tsv"]
        panel.allowsOtherFileTypes = true
        panel.begin { (result) -> Void in
            if result == NSApplication.ModalResponse.OK, let url = panel.url {
                completionHandler([url])
            } else {
                completionHandler(nil)
            }
        }
    }
    
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
//        Used by window.ExternalDownloadHelper which is used by list export.
        if message.name == "download",
           let body = message.body as? NSDictionary,
           let name = body["name"] as? NSString,
           let rawData = body["data"] as? [UInt8] {
            let panel = NSSavePanel()
            panel.allowsOtherFileTypes = true
            panel.nameFieldStringValue = name as String
            panel.allowedFileTypes = ["txt"]
            panel.begin { (result) -> Void in
                if result == NSApplication.ModalResponse.OK, let url = panel.url {
                    let data = Data(rawData)
                    try! data.write(to: url)
                }
            }
        }
    }

    @available(macOS 12.0, *)
    func webView(_ webView: WKWebView, requestMediaCapturePermissionFor origin: WKSecurityOrigin, initiatedByFrame frame: WKFrameInfo, type: WKMediaCaptureType, decisionHandler: @escaping (WKPermissionDecision) -> Void) {
//        macOS will already show a permission prompt.
//        This code prevents WKWebView from showing an additional permission prompt each time.
        decisionHandler(WKPermissionDecision.grant)
    }
    
    func webViewDidClose(_ webView: WKWebView) {
//        Implements window.close()
        self.view.window?.close();
    }
}