FileSaver-eligrey

一个允许在客户端保存文件的解决方案

此腳本不應該直接安裝,它是一個供其他腳本使用的函式庫。欲使用本函式庫,請在腳本 metadata 寫上: // @require https://update.cn-greasyfork.org/scripts/450828/1090308/FileSaver-eligrey.js

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

function enableSaveAs() {

    /*
    * FileSaver.js
    * A saveAs() FileSaver implementation.
    *
    * By Eli Grey, http://eligrey.com
    *
    * License : https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md (MIT)
    * source  : http://purl.eligrey.com/github/FileSaver.js
    */

    // The one and only way of getting global scope in all environments
    // https://stackoverflow.com/q/3277182/1008999
    var _global = typeof window === 'object' && window.window === window
        ? window : typeof self === 'object' && self.self === self
            ? self : typeof global === 'object' && global.global === global
                ? global
                : this

    function bom(blob, opts) {
        if (typeof opts === 'undefined') opts = { autoBom: false }
        else if (typeof opts !== 'object') {
            console.warn('Deprecated: Expected third argument to be a object')
            opts = { autoBom: !opts }
        }

        // prepend BOM for UTF-8 XML and text/* types (including HTML)
        // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
        if (opts.autoBom && /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
            return new Blob([String.fromCharCode(0xFEFF), blob], { type: blob.type })
        }
        return blob
    }

    function download(url, name, opts) {
        var xhr = new XMLHttpRequest()
        xhr.open('GET', url)
        xhr.responseType = 'blob'
        xhr.onload = function () {
            saveAs(xhr.response, name, opts)
        }
        xhr.onerror = function () {
            console.error('could not download file')
        }
        xhr.send()
    }

    function corsEnabled(url) {
        var xhr = new XMLHttpRequest()
        // use sync to avoid popup blocker
        xhr.open('HEAD', url, false)
        try {
            xhr.send()
        } catch (e) { }
        return xhr.status >= 200 && xhr.status <= 299
    }

    // `a.click()` doesn't work for all browsers (#465)
    function click(node) {
        try {
            node.dispatchEvent(new MouseEvent('click'))
        } catch (e) {
            var evt = document.createEvent('MouseEvents')
            evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80,
                20, false, false, false, false, 0, null)
            node.dispatchEvent(evt)
        }
    }

    // Detect WebView inside a native macOS app by ruling out all browsers
    // We just need to check for 'Safari' because all other browsers (besides Firefox) include that too
    // https://www.whatismybrowser.com/guides/the-latest-user-agent/macos
    var isMacOSWebView = _global.navigator && /Macintosh/.test(navigator.userAgent) && /AppleWebKit/.test(navigator.userAgent) && !/Safari/.test(navigator.userAgent)

    var saveAs = _global.saveAs || (
        // probably in some web worker
        (typeof window !== 'object' || window !== _global)
            ? function saveAs() { /* noop */ }

            // Use download attribute first if possible (#193 Lumia mobile) unless this is a macOS WebView
            : ('download' in HTMLAnchorElement.prototype && !isMacOSWebView)
                ? function saveAs(blob, name, opts) {
                    var URL = _global.URL || _global.webkitURL
                    // Namespace is used to prevent conflict w/ Chrome Poper Blocker extension (Issue #561)
                    var a = document.createElementNS('http://www.w3.org/1999/xhtml', 'a')
                    name = name || blob.name || 'download'

                    a.download = name
                    a.rel = 'noopener' // tabnabbing

                    // TODO: detect chrome extensions & packaged apps
                    // a.target = '_blank'

                    if (typeof blob === 'string') {
                        // Support regular links
                        a.href = blob
                        if (a.origin !== location.origin) {
                            corsEnabled(a.href)
                                ? download(blob, name, opts)
                                : click(a, a.target = '_blank')
                        } else {
                            click(a)
                        }
                    } else {
                        // Support blobs
                        a.href = URL.createObjectURL(blob)
                        setTimeout(function () { URL.revokeObjectURL(a.href) }, 4E4) // 40s
                        setTimeout(function () { click(a) }, 0)
                    }
                }

                // Use msSaveOrOpenBlob as a second approach
                : 'msSaveOrOpenBlob' in navigator
                    ? function saveAs(blob, name, opts) {
                        name = name || blob.name || 'download'

                        if (typeof blob === 'string') {
                            if (corsEnabled(blob)) {
                                download(blob, name, opts)
                            } else {
                                var a = document.createElement('a')
                                a.href = blob
                                a.target = '_blank'
                                setTimeout(function () { click(a) })
                            }
                        } else {
                            navigator.msSaveOrOpenBlob(bom(blob, opts), name)
                        }
                    }

                    // Fallback to using FileReader and a popup
                    : function saveAs(blob, name, opts, popup) {
                        // Open a popup immediately do go around popup blocker
                        // Mostly only available on user interaction and the fileReader is async so...
                        popup = popup || open('', '_blank')
                        if (popup) {
                            popup.document.title =
                                popup.document.body.innerText = 'downloading...'
                        }

                        if (typeof blob === 'string') return download(blob, name, opts)

                        var force = blob.type === 'application/octet-stream'
                        var isSafari = /constructor/i.test(_global.HTMLElement) || _global.safari
                        var isChromeIOS = /CriOS\/[\d]+/.test(navigator.userAgent)

                        if ((isChromeIOS || (force && isSafari) || isMacOSWebView) && typeof FileReader !== 'undefined') {
                            // Safari doesn't allow downloading of blob URLs
                            var reader = new FileReader()
                            reader.onloadend = function () {
                                var url = reader.result
                                url = isChromeIOS ? url : url.replace(/^data:[^;]*;/, 'data:attachment/file;')
                                if (popup) popup.location.href = url
                                else location = url
                                popup = null // reverse-tabnabbing #460
                            }
                            reader.readAsDataURL(blob)
                        } else {
                            var URL = _global.URL || _global.webkitURL
                            var url = URL.createObjectURL(blob)
                            if (popup) popup.location = url
                            else location.href = url
                            popup = null // reverse-tabnabbing #460
                            setTimeout(function () { URL.revokeObjectURL(url) }, 4E4) // 40s
                        }
                    }
    )

    _global.saveAs = saveAs.saveAs = saveAs

    if (typeof module !== 'undefined') {
        module.exports = saveAs;
    }
}