/**
 * @class Ext.ElementLoader
 * A class used to load remote content to an Element. Sample usage:
 * <pre><code>
Ext.get('el').load({
    url: 'myPage.php',
    scripts: true,
    params: {
        id: 1
    }
});
 * </code></pre>
 * <p>
 * In general this class will not be instanced directly, rather the {@link Ext.core.Element#load} method
 * will be used.
 * </p>
 */

Ext.define('Ext.ElementLoader', {

   
/* Begin Definitions */

    mixins
: {
        observable
: 'Ext.util.Observable'
   
},

    uses
: [
       
'Ext.data.Connection',
       
'Ext.Ajax'
   
],
   
    statics
: {
       
Renderer: {
           
Html: function(loader, response, active){
                loader
.getTarget().update(response.responseText, active.scripts === true);
               
return true;
           
}
       
}    
   
},

   
/* End Definitions */

    /**
     * @cfg {String} url The url to retrieve the content from. Defaults to <tt>null</tt>.
     */

    url
: null,

    /**
     * @cfg {Object} params Any params to be attached to the Ajax request. These parameters will
     * be overridden by any params in the load options. Defaults to <tt>null</tt>.
     */

    params
: null,

    /**
     * @cfg {Object} baseParams Params that will be attached to every request. These parameters
     * will not be overridden by any params in the load options. Defaults to <tt>null</tt>.
     */

    baseParams
: null,

    /**
     * @cfg {Boolean/Object} autoLoad True to have the loader make a request as soon as it is created. Defaults to <tt>false</tt>.
     * This argument can also be a set of options that will be passed to {@link #load} is called.
     */

    autoLoad
: false,

    /**
     * @cfg {Mixed} target The target element for the loader. It can be the DOM element, the id or an Ext.Element.
     */

    target
: null,

    /**
     * @cfg {Mixed} loadMask True or a string to show when the element is loading.
     */

    loadMask
: false,

    /**
     * @cfg {Object} ajaxOptions Any additional options to be passed to the request, for example timeout or headers. Defaults to <tt>null</tt>.
     */

    ajaxOptions
: null,
   
    /**
     * @cfg {Boolean} scripts True to parse any inline script tags in the response.
     */

    scripts
: false,

    /**
     * @cfg {Function} success A function to be called when a load request is successful.
     */


    /**
     * @cfg {Function} failure A function to be called when a load request fails.
     */


    /**
     * @cfg {Object} scope The scope to execute the {@link #success} and {@link #failure} functions in.
     */

   
    /**
     * @cfg {Function} renderer A custom function to render the content to the element. The passed parameters
     * are
     * <ul>
     * <li>The loader</li>
     * <li>The response</li>
     * <li>The active request</li>
     * </ul>
     */


    isLoader
: true,

    constructor
: function(config) {
       
var me = this,
            autoLoad
;
       
        config
= config || {};
       
Ext.apply(me, config);
        me
.setTarget(me.target);
        me
.addEvents(
            /**
             * @event beforeload
             * Fires before a load request is made to the server.
             * Returning false from an event listener can prevent the load
             * from occurring.
             * @param {Ext.ElementLoader} this
             * @param {Object} options The options passed to the request
             */

           
'beforeload',

            /**
             * @event exception
             * Fires after an unsuccessful load.
             * @param {Ext.ElementLoader} this
             * @param {Object} response The response from the server
             * @param {Object} options The options passed to the request
             */

           
'exception',

            /**
             * @event exception
             * Fires after a successful load.
             * @param {Ext.ElementLoader} this
             * @param {Object} response The response from the server
             * @param {Object} options The options passed to the request
             */

           
'load'
       
);

       
// don't pass config because we have already applied it.
        me
.mixins.observable.constructor.call(me);

       
if (me.autoLoad) {
            autoLoad
= me.autoLoad;
           
if (autoLoad === true) {
                autoLoad
= {};
           
}
            me
.load(autoLoad);
       
}
   
},

    /**
     * Set an {Ext.Element} as the target of this loader. Note that if the target is changed,
     * any active requests will be aborted.
     * @param {Mixed} target The element
     */

    setTarget
: function(target){
       
var me = this;
        target
= Ext.get(target);
       
if (me.target && me.target != target) {
            me
.abort();
       
}
        me
.target = target;
   
},

    /**
     * Get the target of this loader.
     * @return {Ext.Component} target The target, null if none exists.
     */

    getTarget
: function(){
       
return this.target || null;
   
},

    /**
     * Aborts the active load request
     */

    abort
: function(){
       
var active = this.active;
       
if (active !== undefined) {
           
Ext.Ajax.abort(active.request);
           
if (active.mask) {
               
this.removeMask();
           
}
           
delete this.active;
       
}
   
},
   
    /**
     * Remove the mask on the target
     * @private
     */

    removeMask
: function(){
       
this.target.unmask();
   
},
   
    /**
     * Add the mask on the target
     * @private
     * @param {Mixed} mask The mask configuration
     */

    addMask
: function(mask){
       
this.target.mask(mask === true ? null : mask);
   
},

    /**
     * Load new data from the server.
     * @param {Object} options The options for the request. They can be any configuration option that can be specified for
     * the class, with the exception of the target option. Note that any options passed to the method will override any
     * class defaults.
     */

    load
: function(options) {
       
//<debug>
       
if (!this.target) {
           
Ext.Error.raise('A valid target is required when loading content');
       
}
       
//</debug>

        options
= Ext.apply({}, options);

       
var me = this,
            target
= me.target,
            mask
= Ext.isDefined(options.loadMask) ? options.loadMask : me.loadMask,
            params
= Ext.apply({}, options.params),
            ajaxOptions
= Ext.apply({}, options.ajaxOptions),
            callback
= options.callback || me.callback,
            scope
= options.scope || me.scope || me,
            request
;

       
Ext.applyIf(ajaxOptions, me.ajaxOptions);
       
Ext.applyIf(options, ajaxOptions);

       
Ext.applyIf(params, me.params);
       
Ext.apply(params, me.baseParams);

       
Ext.applyIf(options, {
            url
: me.url
       
});

       
//<debug>
       
if (!options.url) {
           
Ext.Error.raise('You must specify the URL from which content should be loaded');
       
}
       
//</debug>

       
Ext.apply(options, {
            scope
: me,
            params
: params,
            callback
: me.onComplete
       
});

       
if (me.fireEvent('beforeload', me, options) === false) {
           
return;
       
}

       
if (mask) {
            me
.addMask(mask);
       
}

        request
= Ext.Ajax.request(options);
        me
.active = {
            request
: request,
            options
: options,
            mask
: mask,
            scope
: scope,
            callback
: callback,
            success
: options.success || me.success,
            failure
: options.failure || me.failure,
            renderer
: options.renderer || me.renderer,
            scripts
: Ext.isDefined(options.scripts) ? options.scripts : me.scripts
       
};
        me
.setOptions(me.active, options);
   
},
   
    /**
     * Set any additional options on the active request
     * @private
     * @param {Object} active The active request
     * @param {Object} options The initial options
     */

    setOptions
: Ext.emptyFn,

    /**
     * Parse the response after the request completes
     * @private
     * @param {Object} options Ajax options
     * @param {Boolean} success Success status of the request
     * @param {Object} response The response object
     */

    onComplete
: function(options, success, response) {
       
var me = this,
            active
= me.active,
            scope
= active.scope,
            renderer
= me.getRenderer(active.renderer);


       
if (success) {
            success
= renderer.call(me, me, response, active);
       
}

       
if (success) {
           
Ext.callback(active.success, scope, [me, response, options]);
            me
.fireEvent('load', me, response, options);
       
} else {
           
Ext.callback(active.failure, scope, [me, response, options]);
            me
.fireEvent('exception', me, response, options);
       
}
       
Ext.callback(active.callback, scope, [me, success, response, options]);

       
if (active.mask) {
            me
.removeMask();
       
}

       
delete me.active;
   
},

    /**
     * Gets the renderer to use
     * @private
     * @param {String/Function} renderer The renderer to use
     * @return {Function} A rendering function to use.
     */

    getRenderer
: function(renderer){
       
if (Ext.isFunction(renderer)) {
           
return renderer;
       
}
       
return this.statics().Renderer.Html;
   
},
   
    /**
     * Automatically refreshes the content over a specified period.
     * @param {Number} interval The interval to refresh in ms.
     * @param {Object} options (optional) The options to pass to the load method. See {@link #load}
     */

    startAutoRefresh
: function(interval, options){
       
var me = this;
        me
.stopAutoRefresh();
        me
.autoRefresh = setInterval(function(){
            me
.load(options);
       
}, interval);
   
},
   
    /**
     * Clears any auto refresh. See {@link #startAutoRefresh}.
     */

    stopAutoRefresh
: function(){
        clearInterval
(this.autoRefresh);
       
delete this.autoRefresh;
   
},
   
    /**
     * Checks whether the loader is automatically refreshing. See {@link #startAutoRefresh}.
     * @return {Boolean} True if the loader is automatically refreshing
     */

    isAutoRefreshing
: function(){
       
return Ext.isDefined(this.autoRefresh);
   
},

    /**
     * Destroys the loader. Any active requests will be aborted.
     */

    destroy
: function(){
       
var me = this;
        me
.stopAutoRefresh();
       
delete me.target;
        me
.abort();
        me
.clearListeners();
   
}
});