    /** * This singleton has methods for file manipulation. * * It allows file saving using browser features or remote server calls. * * Call {@link #saveAs} to save text files or {@link #saveBinaryAs} to save binary files. * If the browser doesn't support file saving then those functions will upload * the file content to the server address provided in {@link #url}. * * The script from the default {@link #url} has a 5Mb upload limitation for file content. * In the "server" folder of the `exporter` package there are examples of * scripts that could be used to implement an in-house server. * * **Note:** When using server side download browser pop-ups should NOT be blocked. */Ext.define('Ext.exporter.File', {    singleton: true,     requires: [        'Ext.promise.Promise',        'Ext.Deferred'    ],     textPopupBlocker: 'The file was not saved because pop-up blocker might be enabled! Please check your browser settings.',     /**     * @property {String} url      *     * Address of the server that supports file downloading. Check out the scripts     * from the "server" folder of the `exporter` package if an in-house server     * needs to be implemented.     */    url: 'https://exporter.sencha.com',     /**     * @property {Boolean} forceDownload      *     * Set to `true` to always download files from the server {@link #url} instead of saving     * files using browser features.     */    forceDownload: false,     /**     * Save a binary file locally using either [Blob][1] or server side script.     *     * [1]: https://developer.mozilla.org/en/docs/Web/API/Blob     *     * Browser compatibility when using [Blob][1]:     *     * - Firefox 20+: max blob size 800 MB     * - Chrome: max blob size 500 MB     * - Chrome for Android: max blob size 500 MB     * - Edge: max blob size n/a     * - IE 10+: max blob size 600 MB     * - Opera 15+: max blob size 500 MB     *     * For all other browsers it falls back to server side script which means that     * the file content is uploaded to the server script defined in {@link #url} and comes     * back to the browser as a file download.     *     * @param {String} content File content     * @param {String} filename Name of the file including the extension     * @param {String} [mimeType='application/octet-stream'] Mime type of the file     * @return {Ext.promise.Promise}     */    saveBinaryAs: function(content, filename, mimeType){        var me = this,            pt = Ext.platformTags,            force = me.forceDownload || pt.phone || pt.tablet,            saveAs = me.downloadBinaryAs;         if(!force && !Ext.isSafari && me.saveBlobAs){            saveAs = me.saveBlobAs;        }         // The method saveBlobAs exists only if the browser supports Blob         return saveAs.call(me, content, filename, mimeType);    },     /**     * Save a binary file using a server side script. The file content, file name and mime-type are uploaded     * to the server side script and a download is forced from the server.     *     * This method can be used when the browser doesn't support [Blobs][1].     *     * [1]: https://developer.mozilla.org/en/docs/Web/API/Blob     *     * **Note** Browsers pop-ups should NOT be blocked for this feature to work as expected.     *     * @param {String} content File content     * @param {String} filename Name of the file including the extension     * @param {String} [mimeType='application/octet-stream'] Mime type of the file     * @return {Ext.promise.Promise}     */    downloadBinaryAs: function(content, filename, mimeType){        var deferred = new Ext.Deferred(),            inputs, markup, win;         //<debug>         if (!this.url) {            Ext.raise('Cannot download file since no URL was defined!');            return;        }        //</debug>          markup = Ext.dom.Helper.markup({            tag: 'html',            children: [                {tag: 'head'},                {                    tag: 'body',                    children: [                        {                            tag: 'form',                            method: 'POST',                            action: this.url,                            children: [{                                tag: 'input',                                type: 'hidden',                                name: 'content',                                value: Ext.util.Base64.encode(content)                            }, {                                tag: 'input',                                type: 'hidden',                                name: 'filename',                                value: filename                            }, {                                tag: 'input',                                type: 'hidden',                                name: 'mime',                                value: mimeType || 'application/octet-stream'                            }]                        },                        {                            tag: 'script',                            type: 'text/javascript',                            children: 'document.getElementsByTagName("form")[0].submit();'                        }                    ]                }            ]        });         win = window.open('', 'FileDownload_' + Date.now());        if (win) {            win.document.write(markup);            deferred.resolve();        } else {            deferred.reject(this.textPopupBlocker);        }        return deferred.promise;    }     /**     * Save a text file locally using the content and name provided.     *     * Browser	compatibility:     *     * - Firefox 20+: max blob size 800 MB     * - Chrome: max blob size 500 MB     * - Chrome for Android: max blob size 500 MB     * - Edge: max blob size n/a     * - IE 10+: max blob size 600 MB     * - IE < 10: Files are saved as text/html and max file size n/a     * - Opera 15+: max blob size 500 MB     * - Opera < 15: max blob size n/a     * - Safari 6.1+: max blob size n/a; Blobs may be opened instead of saved sometimes—you may have     * to direct your Safari users to manually press ⌘+S to save the file after it is opened.     * Using the application/octet-stream MIME type to force downloads can cause issues in Safari.     * - Safari < 6: max blob size n/a     *     * **Note:** For IE < 10, available file extensions are ".htm/.html/.txt", any other text based file will     * be appended with ".txt" file extension automatically. For example, "test.csv" will be saved as "test_csv.txt".     *     * @method saveAs     * @param {String} content File content     * @param {String} filename Name of the file including the extension     * @param {String} [charset='UTF-8'] File's charset     * @return {Ext.promise.Promise}     */     /**     * Save a binary file locally using [Blobs][1].     *     * Browser compatibility:     *     * - Firefox 20+: max blob size 800 MB     * - Chrome: max blob size 500 MB     * - Chrome for Android: max blob size 500 MB     * - Edge: max blob size n/a     * - IE 10+: max blob size 600 MB     * - Opera 15+: max blob size 500 MB     *     * [1]: https://developer.mozilla.org/en/docs/Web/API/Blob     *     * @method saveBlobAs     * @param {String} content File content     * @param {String} filename Name of the file including the extension     * @param {String} [mimeType='application/octet-stream'] Mime type of the file     * @return {Ext.promise.Promise}     * @private     */ }, function(File){    /* FileSaver.js     *  A saveAs() & saveTextAs() FileSaver implementation.     * 1.1.20160328     *     *  Modify by Brian Chen     * By Eli Grey, http://eligrey.com     * License: MIT     *   See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md     */     /*global self */    /*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */     /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */    var navigator = window.navigator,        saveAs = window.saveAs || (function(view) {                "use strict";                // IE <10 is explicitly unsupported                 if (typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {                    return;                }                var                    doc = view.document                // only get URL when necessary in case Blob.js hasn't overridden it yet                     , get_URL = function() {                        return view.URL || view.webkitURL || view;                    }                    , save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")                    , can_use_save_link = "download" in save_link                    , click = function(node) {                        var event = new MouseEvent("click");                        node.dispatchEvent(event);                    }                    , is_safari = /Version\/[\d\.]+.*Safari/.test(navigator.userAgent)                    , webkit_req_fs = view.webkitRequestFileSystem                    , req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem                    , throw_outside = function(ex) {                        (view.setImmediate || view.setTimeout)(function() {                            throw ex;                        }, 0);                    }                    , force_saveable_type = "application/octet-stream"                    , fs_min_size = 0                // the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to                     , arbitrary_revoke_timeout = 1000 * 40 // in ms                     , revoke = function(file) {                        var revoker = function() {                            if (typeof file === "string") { // file is an object URL                                 get_URL().revokeObjectURL(file);                            } else { // file is a File                                 file.remove();                            }                        };                        /* // Take note W3C:                         var                         uri = typeof file === "string" ? file : file.toURL()                         , revoker = function(evt) {                         // idealy DownloadFinishedEvent.data would be the URL requested                         if (evt.data === uri) {                         if (typeof file === "string") { // file is an object URL                         get_URL().revokeObjectURL(file);                         } else { // file is a File                         file.remove();                         }                         }                         }                         ;                         view.addEventListener("downloadfinished", revoker);                         */                        setTimeout(revoker, arbitrary_revoke_timeout);                    }                    , dispatch = function(filesaver, event_types, event) {                        event_types = [].concat(event_types);                        var i = event_types.length;                        while (i--) {                            var listener = filesaver["on" + event_types[i]];                            if (typeof listener === "function") {                                try {                                    listener.call(filesaver, event || filesaver);                                } catch (ex) {                                    throw_outside(ex);                                }                            }                        }                    }                    , auto_bom = function(blob) {                        // prepend BOM for UTF-8 XML and text/* types (including HTML)                         if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {                            return new Blob(["\ufeff", blob], {type: blob.type});                        }                        return blob;                    }                    , FileSaver = function(blob, name, no_auto_bom) {                        if (!no_auto_bom) {                            blob = auto_bom(blob);                        }                        // First try a.download, then web filesystem, then object URLs                         var                            filesaver = this                            , type = blob.type                            , blob_changed = false                            , object_url                            , target_view                            , dispatch_all = function() {                                dispatch(filesaver, "writestart progress write writeend".split(" "));                            }                        // on any filesys errors revert to saving with object URLs                             , fs_error = function() {                                if (target_view && is_safari && typeof FileReader !== "undefined") {                                    // Safari doesn't allow downloading of blob urls                                     var reader = new FileReader();                                    reader.onloadend = function() {                                        var base64Data = reader.result;                                        target_view.location.href = "data:attachment/file" + base64Data.slice(base64Data.search(/[,;]/));                                        filesaver.readyState = filesaver.DONE;                                        dispatch_all();                                    };                                    reader.readAsDataURL(blob);                                    filesaver.readyState = filesaver.INIT;                                    return;                                }                                // don't create more object URLs than needed                                 if (blob_changed || !object_url) {                                    object_url = get_URL().createObjectURL(blob);                                }                                if (target_view) {                                    target_view.location.href = object_url;                                } else {                                    var new_tab = view.open(object_url, "_blank");                                    if (new_tab === undefined && is_safari) {                                        //Apple do not allow window.open, see http://bit.ly/1kZffRI                                         view.location.href = object_url                                    }                                }                                filesaver.readyState = filesaver.DONE;                                dispatch_all();                                revoke(object_url);                            }                            , abortable = function(func) {                                return function() {                                    if (filesaver.readyState !== filesaver.DONE) {                                        return func.apply(this, arguments);                                    }                                };                            }                            , create_if_not_found = {create: true, exclusive: false}                            , slice                            ;                        filesaver.readyState = filesaver.INIT;                        if (!name) {                            name = "download";                        }                        if (can_use_save_link) {                            object_url = get_URL().createObjectURL(blob);                            setTimeout(function() {                                save_link.href = object_url;                                save_link.download = name;                                click(save_link);                                dispatch_all();                                revoke(object_url);                                filesaver.readyState = filesaver.DONE;                            });                            return;                        }                        // Object and web filesystem URLs have a problem saving in Google Chrome when                         // viewed in a tab, so I force save with application/octet-stream                         // http://code.google.com/p/chromium/issues/detail?id=91158                         // Update: Google errantly closed 91158, I submitted it again:                         // https://code.google.com/p/chromium/issues/detail?id=389642                         if (view.chrome && type && type !== force_saveable_type) {                            slice = blob.slice || blob.webkitSlice;                            blob = slice.call(blob, 0, blob.size, force_saveable_type);                            blob_changed = true;                        }                        // Since I can't be sure that the guessed media type will trigger a download                         // in WebKit, I append .download to the filename.                         // https://bugs.webkit.org/show_bug.cgi?id=65440                         if (webkit_req_fs && name !== "download") {                            name += ".download";                        }                        if (type === force_saveable_type || webkit_req_fs) {                            target_view = view;                        }                        if (!req_fs) {                            fs_error();                            return;                        }                        fs_min_size += blob.size;                        req_fs(view.TEMPORARY, fs_min_size, abortable(function(fs) {                            fs.root.getDirectory("saved", create_if_not_found, abortable(function(dir) {                                var save = function() {                                    dir.getFile(name, create_if_not_found, abortable(function(file) {                                        file.createWriter(abortable(function(writer) {                                            writer.onwriteend = function(event) {                                                target_view.location.href = file.toURL();                                                filesaver.readyState = filesaver.DONE;                                                dispatch(filesaver, "writeend", event);                                                revoke(file);                                            };                                            writer.onerror = function() {                                                var error = writer.error;                                                if (error.code !== error.ABORT_ERR) {                                                    fs_error();                                                }                                            };                                            "writestart progress write abort".split(" ").forEach(function(event) {                                                writer["on" + event] = filesaver["on" + event];                                            });                                            writer.write(blob);                                            filesaver.abort = function() {                                                writer.abort();                                                filesaver.readyState = filesaver.DONE;                                            };                                            filesaver.readyState = filesaver.WRITING;                                        }), fs_error);                                    }), fs_error);                                };                                dir.getFile(name, {create: false}, abortable(function(file) {                                    // delete file if it already exists                                     file.remove();                                    save();                                }), abortable(function(ex) {                                    if (ex.code === ex.NOT_FOUND_ERR) {                                        save();                                    } else {                                        fs_error();                                    }                                }));                            }), fs_error);                        }), fs_error);                    }                    , FS_proto = FileSaver.prototype                    , saveAs = function(blob, name, no_auto_bom) {                        return new FileSaver(blob, name, no_auto_bom);                    }                    ;                // IE 10+ (native saveAs)                 if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {                    return function(blob, name, no_auto_bom) {                        if (!no_auto_bom) {                            blob = auto_bom(blob);                        }                        return navigator.msSaveOrOpenBlob(blob, name || "download");                    };                }                 FS_proto.abort = function() {                    var filesaver = this;                    filesaver.readyState = filesaver.DONE;                    dispatch(filesaver, "abort");                };                FS_proto.readyState = FS_proto.INIT = 0;                FS_proto.WRITING = 1;                FS_proto.DONE = 2;                 FS_proto.error =                    FS_proto.onwritestart =                        FS_proto.onprogress =                            FS_proto.onwrite =                                FS_proto.onabort =                                    FS_proto.onerror =                                        FS_proto.onwriteend =                                            null;                 return saveAs;            }(                typeof self !== "undefined" && self                || typeof window !== "undefined" && window                || this.content            ));// `self` is undefined in Firefox for Android content script context // while `this` is nsIContentFrameMessageManager // with an attribute `content` that corresponds to the window      if (typeof module !== "undefined" && module.exports) {        module.exports.saveAs = saveAs;    } else if ((typeof define !== "undefined" && define !== null) && (define.amd !== null)) {        define([], function() {            return saveAs;        });    }     var saveTextAs = window.saveTextAs        || (function (textContent, fileName, charset) {            fileName = fileName || 'download.txt';            charset = charset || 'utf-8';            textContent = (textContent || '').replace(/\r?\n/g, "\r\n");            if (saveAs && Blob) {                var blob = new Blob([textContent], { type: "text/plain;charset=" + charset });                saveAs(blob, fileName);                return true;            } else {//IE9-                 var saveTxtWindow = window.frames.saveTxtWindow;                if (!saveTxtWindow) {                    saveTxtWindow = document.createElement('iframe');                    saveTxtWindow.id = 'saveTxtWindow';                    saveTxtWindow.style.display = 'none';                    document.body.insertBefore(saveTxtWindow, null);                    saveTxtWindow = window.frames.saveTxtWindow;                    if (!saveTxtWindow) {                        saveTxtWindow = window.open('', '_temp', 'width=100,height=100');                        if (!saveTxtWindow) {                            window.alert('Sorry, download file could not be created.');                            return false;                        }                    }                }                 var doc = saveTxtWindow.document;                doc.open('text/html', 'replace');                doc.charset = charset;                if ( Ext.String.endsWith(fileName, '.htm', true) || Ext.String.endsWith(fileName, '.html', true) ) {                    doc.close();                    doc.body.innerHTML = '\r\n' + textContent + '\r\n';                } else {                    if (!Ext.String.endsWith(fileName, '.txt', true)) fileName += '.txt';                    doc.write(textContent);                    doc.close();                }                 var retValue = doc.execCommand('SaveAs', null, fileName);                saveTxtWindow.close();                return retValue;            }        });     File.saveAs = function(content, filename, charset){        var deferred;         if(this.forceDownload){            return this.downloadBinaryAs(content, filename, 'text/plain');        }else{            deferred = new Ext.Deferred();            if(saveTextAs(content, filename, charset)) {                deferred.resolve();            }else{                deferred.reject();            }            return deferred.promise;        }    };     if(saveAs && Blob){        File.saveBlobAs = function(textContent, fileName, mimeType){            var deferred = new Ext.Deferred();             var uint8 = new Uint8Array(textContent.length),                len = uint8.length,                bType = {type: mimeType || 'application/octet-stream'},                blob, i;             for (i = 0; i < len; i++) {                uint8[i] = textContent.charCodeAt(i);            }             blob = new Blob([uint8], bType);            saveAs(blob, fileName);            deferred.resolve();            return deferred.promise;        };    }});