    /** * This class manages a pending form submit. Instances of this type are created by the * `{@link Ext.data.Connection#request}` method. * @since 6.0.0 */Ext.define('Ext.data.request.Form', {    extend: 'Ext.data.request.Base',    alias:  'request.form',     start: function(data) {        var me = this,            options = me.options,            requestOptions = me.requestOptions;                // Parent will set the timeout         me.callParent([data]);                me.form = me.upload(options.form, requestOptions.url, requestOptions.data, options);                return me;    },     abort: function(force) {        var me = this,            frame;                if (me.isLoading()) {                        try {                frame = me.frame.dom;                                if (frame.stop) {                    frame.stop();                }                else {                    frame.document.execCommand('Stop');                }            }            catch (e) {                // ignore             }        }                me.callParent([force]);                me.onComplete();        me.cleanup();    },        /*     * Clean up any left over information from the form submission.     */    cleanup: function() {        var me = this,            frame = me.frame;                if (frame) {            // onComplete hasn't fired yet if frame != null so need to clean up             frame.un('load', me.onComplete, me);            Ext.removeNode(frame);        }                me.frame = me.form = null;    },        isLoading: function() {        return !!this.frame;    },        /**     * Uploads a form using a hidden iframe.     * @param {String/HTMLElement/Ext.dom.Element} form The form to upload     * @param {String} url The url to post to     * @param {String} params Any extra parameters to pass     * @param {Object} options The initial options     * @private     */    upload: function(form, url, params, options) {        form = Ext.getDom(form);        options = options || {};         var frameDom = document.createElement('iframe'),            frame = Ext.get(frameDom),            id = frame.id,            hiddens = [],            encoding = 'multipart/form-data',            buf = {                target: form.target,                method: form.method,                encoding: form.encoding,                enctype: form.enctype,                action: form.action            },            addField = function(name, value) {                hiddenItem = document.createElement('input');                Ext.fly(hiddenItem).set({                    type: 'hidden',                    value: value,                    name: name                });                form.appendChild(hiddenItem);                hiddens.push(hiddenItem);            },            hiddenItem, obj, value, name, vLen, v, hLen, h, request;         /*         * Originally this behaviour was modified for Opera 10 to apply the secure URL after         * the frame had been added to the document. It seems this has since been corrected in         * Opera so the behaviour has been reverted, the URL will be set before being added.         */        frame.set({            name: id,            cls: Ext.baseCSSPrefix + 'hidden-display',            src: Ext.SSL_SECURE_URL,            tabIndex: -1        });         document.body.appendChild(frameDom);         // This is required so that IE doesn't pop the response up in a new window.         if (document.frames) {            document.frames[id].name = id;        }         Ext.fly(form).set({            target: id,            method: 'POST',            enctype: encoding,            encoding: encoding,            action: url || buf.action        });         // add dynamic params         if (params) {            obj = Ext.Object.fromQueryString(params) || {};             for (name in obj) {                if (obj.hasOwnProperty(name)) {                    value = obj[name];                    if (Ext.isArray(value)) {                        vLen = value.length;                        for (v = 0; v < vLen; v++) {                            addField(name, value[v]);                        }                    } else {                        addField(name, value);                    }                }            }        }                this.frame = frame;                frame.on({            load: this.onComplete,            scope: this,            // Opera introduces multiple 'load' events, so account for extras as well             single: !Ext.isOpera        });         form.submit();         // Restore form to previous settings         Ext.fly(form).set(buf);         for (hLen = hiddens.length, h = 0; h < hLen; h++) {            Ext.removeNode(hiddens[h]);        }         return form;    },     getDoc: function() {        var frame = this.frame.dom;         return (frame && (frame.contentWindow.document || frame.contentDocument)) ||                (window.frames[frame.id] || {}).document;    },     getTimeout: function() {        // For a form post, since it can include large file uploads, we do not use the         // default timeout from the owner. Only explicit timeouts passed in the options         // are meaningful here.         return this.options.timeout;    },     /**     * Callback handler for the upload function. After we've submitted the form via the     * iframe this creates a bogus response object to simulate an XHR and populates its     * responseText from the now-loaded iframe's document body (or a textarea inside the     * body). We then clean up by removing the iframe.     * @private     */    onComplete: function() {        var me = this,            frame = me.frame,            owner = me.owner,            options = me.options,            callback, doc, success, contentNode, response;                // Nulled out frame means onComplete was fired already         if (!frame) {            return;        }                if (me.aborted || me.timedout) {            me.result = response = me.createException();            response.responseXML = null;            response.responseText = '{success:false,message:"' + Ext.String.trim(response.statusText) + '"}';                       response.request = me;            callback = options.failure;            success = false;        }        else {            try {                doc = me.getDoc();                                // bogus response object                 me.result = response = {                    responseText: '',                    responseXML: null,                    request: me                };                                // Opera will fire an extraneous load event on about:blank                 // We want to ignore this since the load event will be fired twice                 if (doc) {                    //TODO: See if this still applies vs Current opera-webkit releases                     if (Ext.isOpera && doc.location == Ext.SSL_SECURE_URL) {                        return;                    }                                        if (doc.body) {                        // Response sent as Content-Type: text/json or text/plain.                         // Browser will embed it in a <pre> element.                         // Note: The statement below tests the result of an assignment.                         if ((contentNode = doc.body.firstChild) && /pre/i.test(contentNode.tagName)) {                            response.responseText = contentNode.textContent || contentNode.innerText;                        }                        // Response sent as Content-Type: text/html. We must still support                         // JSON response wrapped in textarea.                         // Note: The statement below tests the result of an assignment.                         else if ((contentNode = doc.getElementsByTagName('textarea')[0])) {                            response.responseText = contentNode.value;                        }                        // Response sent as Content-Type: text/html with no wrapping. Scrape                         // JSON response out of text                         else {                            response.responseText = doc.body.textContent || doc.body.innerText;                        }                    }                                        //in IE the document may still have a body even if returns XML.                     // TODO What is this about?                     response.responseXML = doc.XMLDocument || doc;                    callback = options.success;                    success = true;                    response.status = 200;                }                else {                    Ext.raise("Could not acquire a suitable connection for the file upload service.");                }            }            catch (e) {                me.result = response = me.createException();                                // Report any error in the message property                 response.status = 400;                response.statusText = (e.message || e.description) + '';                response.responseText = '{success:false,message:"' + Ext.String.trim(response.statusText) + '"}';                response.responseXML = null;                                callback = options.failure;                success = false;            }        }         me.frame = null;        me.success = success;         owner.fireEvent(success ? 'requestcomplete' : 'requestexception', owner, response, options);         Ext.callback(callback, options.scope, [response, options]);        Ext.callback(options.callback, options.scope, [options, success, response]);                owner.onRequestComplete(me);                // Must defer slightly to permit full exit from load event before destruction         Ext.asap(frame.destroy, frame);         me.callParent();    },        destroy: function() {        this.cleanup();        this.callParent();    }});