Source: net/URLLoader.js

//------------------------------------------------------------------------------
// Constructor scope
//------------------------------------------------------------------------------

/** 
 * Creates a URLLoader object.
 * 
 * @constructor
 *
 * @param {Object} [options] A URLRequest object specifying the URL to download. If this parameter is omitted, no load operation begins. If specified, the load operation begins immediately.
 * 
 * @class
 * @classdesc
 * 
 * The URLLoader class downloads data from a URL as text, binary data. It is 
 * useful for downloading text files, XML, or other information to be used in 
 * a dynamic, data-driven application.
 */
rune.net.URLLoader = function(options) {
    
    //--------------------------------------------------------------------------
    // Private properties
    //--------------------------------------------------------------------------
    
    /**
     * Used to convert blob data to DataURL.
     *
     * @type {FileReader}
     * @private
     */
    this.m_fileReader = null;
    
    /**
     * Describes the current request.
     *
     * @type {rune.net.URLRequest}
     * @private
     */
    this.m_request = null;
    
    /**
     * Response data from the current request.
     *
     * @type {rune.net.URLResponse}
     * @private
     */
    this.m_response = null;
    
    /**
     * For data transfer via HTTP.
     *
     * @type {XMLHttpRequest}
     * @private
     */
    this.m_xhr = null;
    
    //--------------------------------------------------------------------------
    // Constructor call
    //--------------------------------------------------------------------------
    
    /**
     * Invokes secondary class constructor.
     */
    this.m_construct(options);
};

//------------------------------------------------------------------------------
// Public prototype getter and setter methods
//------------------------------------------------------------------------------

/**
 * Reference to the URLResponse object that represents the retrieved data. 
 * The reference is null if no data has been retrieved.
 *
 * @member {rune.net.URLResponse} response
 * @memberof rune.net.URLLoader
 * @instance
 * @readonly
 */
Object.defineProperty(rune.net.URLLoader.prototype, "response", {
    /**
     * @this rune.net.URLLoader
     * @ignore
     */
    get : function() {
        return this.m_response;
    }
});

//------------------------------------------------------------------------------
// Public prototype methods (API)
//------------------------------------------------------------------------------

/**
 * Closes the load operation in progress.
 *
 * @return {undefined}
 */
rune.net.URLLoader.prototype.abort = function() {
    this.m_disposeXHR();
    this.m_disposeFileReader();
    this.m_disposeRequest(true);
    this.m_disposeResponse();
};

/**
 * Loads data from the specified URL.
 *
 * @return {undefined}
 */
rune.net.URLLoader.prototype.load = function(options) {
    this.m_constructRequest(options);
};

//------------------------------------------------------------------------------
// Protected prototype methods
//------------------------------------------------------------------------------

/**
 * The class constructor.
 *
 * @returns {undefined}
 * @protected
 * @ignore
 */
rune.net.URLLoader.prototype.m_construct = function(options) {
    if (options) {
        this.load(options);
    }
};

//------------------------------------------------------------------------------
// Private prototype methods
//------------------------------------------------------------------------------

/**
 * Creates a new URLRequest.
 *
 * @param {Object} options URLRequest options.
 *
 * @returns {undefined}
 * @private
 */
rune.net.URLLoader.prototype.m_constructRequest = function(options) {
    this.m_disposeRequest();
    if (this.m_request == null) {
        this.m_request = new rune.net.URLRequest(options);
        if (rune.util.URL.protocol(this.m_request['url']) === "data") {
            this.m_processRequestData();
        } else {
            this.m_processRequestURL();
        }
    } else throw new Error();
};

/**
 * Creates a new URLResponse.
 *
 * @param {String} data Base64 encoded data.
 *
 * @returns {undefined}
 * @private
 */
rune.net.URLLoader.prototype.m_constructResponse = function(data) {
    this.m_disposeResponse();
    if (this.m_response == null && this.m_request != null) {
        this.m_response = new rune.net.URLResponse(data);
        this.m_request.exec("m_onComplete", [this.m_response]);
    } else throw new Error();
};

/**
 * Creates a new XMLHttpRequest object.
 *
 * @returns {undefined}
 * @private
 */
rune.net.URLLoader.prototype.m_constructXHR = function() {
    this.m_disposeXHR();
    if (this.m_xhr == null) {
        this.m_xhr = new XMLHttpRequest();
        this.m_xhr.responseType = "blob";
    }
};

/**
 * Creates a new FileReader object.
 *
 * @returns {undefined}
 * @private
 */
rune.net.URLLoader.prototype.m_constructFileReader = function() {
    this.m_disposeFileReader();
    if (this.m_fileReader == null) {
        this.m_fileReader = new FileReader();
    }
};

/**
 * Deletes the current URLRequest.
 *
 * @param {boolean} [abort] If the abort handler is to be called.
 *
 * @returns {undefined}
 * @private
 */
rune.net.URLLoader.prototype.m_disposeRequest = function(abort) {
    if (this.m_request instanceof rune.net.URLRequest) {
        if (abort == true) {
            this.m_request.exec("m_onAbort", [this]);
        }
        
        this.m_request.dispose();
        this.m_request = null;
    }
};

/**
 * Deletes the current URLResponse.
 *
 * @returns {undefined}
 * @private
 */
rune.net.URLLoader.prototype.m_disposeResponse = function() {
    if (this.m_response instanceof rune.net.URLResponse) {
        this.m_response.dispose();
        this.m_response = null;
    }
};

/**
 * Deletes the current XMLHttpRequest object.
 *
 * @returns {undefined}
 * @private
 */
rune.net.URLLoader.prototype.m_disposeXHR = function() {
    if (this.m_xhr instanceof XMLHttpRequest) {
        this.m_xhr.abort();
        this.m_xhr = null;
    }
};

/**
 * Deletes the current FileReader object.
 *
 * @returns {undefined}
 * @private
 */
rune.net.URLLoader.prototype.m_disposeFileReader = function() {
    if (this.m_fileReader instanceof FileReader) {
        this.m_fileReader.abort();
        this.m_fileReader = null;
    }
};

/**
 * Processes the request to "download" DataURL.
 *
 * @returns {undefined}
 * @private
 */
rune.net.URLLoader.prototype.m_processRequestData = function() {
    if (this.m_request) {
        this.m_constructResponse(this.m_request['url']);
    } else throw new Error();
};

/**
 * Processes the request to retrieve Data from a URL.
 *
 * @returns {undefined}
 * @private
 */
rune.net.URLLoader.prototype.m_processRequestURL = function() {
    var m_this = this;
    this.m_constructXHR();
    if (this.m_xhr) {
        this.m_xhr.onload = function(event) {
            if (event.target.status === 200) {
                m_this.m_processBlob(event.target.response);
            } else {
                m_this.m_execErrorProcess();
            }
        };
        
        this.m_xhr.onerror = function() {
            m_this.m_execErrorProcess();
        };
        
        this.m_xhr.open("GET", this.m_request['url'], true);
        this.m_xhr.send();
    } else throw new Error();
};

/**
 * Converts a Blob to Base64 string.
 *
 * @param {Blob} blob Blob data.
 *
 * @returns {undefined}
 * @private
 * @suppress {checkTypes}
 */
rune.net.URLLoader.prototype.m_processBlob = function(blob) {
    var m_this = this;
    this.m_constructFileReader();
    if (this.m_fileReader) {
        this.m_fileReader.onloadend = function(event) {
            m_this.m_constructResponse(event.target.result);
        };
        
        this.m_fileReader.readAsDataURL(blob);
    } else throw new Error();
};

/**
 * Execute the error handler.
 *
 * @returns {undefined}
 * @private
 */
rune.net.URLLoader.prototype.m_execErrorProcess = function() {
    if (this.m_request) {
        this.m_request.exec("m_onError", [this]);
    } else throw new Error();
};