/**
 * Loader
 *      An extention to the Ajax class that allows for better parsing of the
 *      Response Text.
 * 
 * @usage
 *      var myLoader = new Loader(url, formId, {'options': 'options'});
 *
 * @todo
 *      Styles handling.
 *      More diverse response object.
 *
 * @example
 *      new Loader('test.htm', {onLoad: function(response) {alert(response.title);}});
 *
 * @author Josh Zeigler <jzeigler@resource.com>
 * @author Toby Miller <tmiller@resource.com>
 */

var Loader = Ajax.extend({
    /**
     * defaultOptions
     * Options used in typical implementations
     *
     * method:      get or post
     * applyStyles: true or false
     * onLoad:      callback
     * runScripts:  automatically run scripts before onLoad
     * applyStypes: automatically apply styles before onLoad
     */
    defaultOptions: {
        'method': 'get',
        'applyStyles': true,
        'onLoad': Class.empty,
        'runScripts': false,
        'applyStyles': true
    },
    
    /**
     * regexp
     * Regular expressions used for parsing
     *
     * html:        strips off the html tags
     * title:       extracts the page title
     * body:        extracts the body
     * onload:      extracts the onload calls from the body tag
     * scripts:     extracts all of the inline script blocks
     * extScripts:  extracts all of the external script blocks
     * styles:      extracts all of the inline style blocks
     * extStyles:   extracts all of the external stylesheets
     */
    regexp: {
        'title':        /\<title[^\>]*\>([\s\S]*?)\<\/title\>/i,
        'body':         /\<body[^\>]*\>([\s\S]*?)\<\/body\>/i,
        'onload':       /\<body[^\>]+onload=\"([^\"]+)\"[^\>]*\>/i,
        'scripts':      /\<script([^\>]*)\>([\s\S]*?)\<\/script\>/i,
        'scriptSrc':    /src=\"([^\"]+)\"/i,
        'scriptType':   /type=\"([^\"]+)\"/i,
        'styles':       /\<style([^\>]*)\>([\s\S]*?)\<\/style\>/i,
        'links':        /\<link([^\>]*)\>/i,
        'cssRel':       /rel=\"([^\"]+)\"/i,
        'cssType':      /type=\"([^\"]+)\"/i,
        'cssMedia':     /media=\"([^\"]+)\"/i,
        'cssHref':      /href=\"([^\"]+)\"/i
    },
    //"
    /**
     * initialize
     * Initialize an instance of the Loader object
     *
     * @param string url
     * @param mixed array or object representation of options
     * @return void
     */
    initialize: function(url, options) {
        this.setOptions($merge(this.defaultOptions, options));
        //if ($defined(this.options.data)) this.data = this.options.data;
        this.parsed = {
            'text':     null,
            'xml':      null,
            'title':    null,
            'body':     null,
            'onload':   null,
            'scripts':  [],
            'styles':   []
        };
        this.parent(url, {'onComplete': this.parseResponse.bind(this)});
    },
    
    /**
     * parseRespone
     * Parse the returned response
     *
     * @param string response text
     * @param string response xml
     * @return void
     */
    parseResponse: function(responseText, responseXML) {
        this.parsed.text = responseText;
        this.parsed.xml = responseXML;
        
        while (this.regexp.scripts.test(responseText)) {
            var scripts = this.regexp.scripts.exec(responseText);
            if (scripts.length > 0) {
                this.parsed.scripts.push({
                    'type': (type = this.regexp.scriptType.exec(scripts[1])) ? type[1] : null,
                    'src':  (src = this.regexp.scriptSrc.exec(scripts[1])) ? src[1] : null,
                    'code': scripts[2]
                });
            }
            responseText = responseText.replace(this.regexp.scripts, '');
        }

        while (this.regexp.links.test(responseText)) {
            var links = this.regexp.links.exec(responseText);
            if (links.length > 0) {
                this.parsed.styles.push({
                    'rel':      (rel = this.regexp.cssRel.exec(links[1])) ? rel[1] : '',
                    'type':     (type = this.regexp.cssType.exec(links[1])) ? type[1] : '',
                    'media':    (media = this.regexp.cssMedia.exec(links[1])) ? media[1] : '',
                    'href':     (href = this.regexp.cssHref.exec(links[1])) ? href[1] : '',
                    'code':     links[2]
                });
            }
            responseText = responseText.replace(this.regexp.links, '');
        }
        
        while (this.regexp.styles.test(responseText)) {
            var styles = this.regexp.styles.exec(responseText);
            if (styles.length > 0) {
                this.parsed.styles.push({
                    'type':     (type = this.regexp.cssType.exec(styles[1])) ? type[1] : '',
                    'media':    (media = this.regexp.cssMedia.exec(styles[1])) ? media[1] : '',
                    'href':     (href = this.regexp.cssHref.exec(styles[1])) ? href[1] : '',
                    'code':     styles[2]
                });
            }
            responseText = responseText.replace(this.regexp.styles, '');
        }
        
        if(this.regexp.title.test(responseText)) this.parsed.title = this.regexp.title.exec(responseText)[1];
        if(this.regexp.onload.test(responseText)) this.parsed.onload = this.regexp.onload.exec(responseText)[1];
        if(this.regexp.body.test(responseText)) this.parsed.body = this.regexp.body.exec(responseText)[1];
        
        if(this.options.applyStyles) this.applyStyles();
        this.prepareScripts();
    },
    
    prepareScripts: function() {
        var completed = function(idx) {
             {
                if(this.options.runScripts) this.executeScripts();
                this.fireEvent('onLoad', this.parsed);
            }
        }.bind(this);
        
        var getRemoteScript = function(src, idx) {                
            var onComplete = function(responseText) {
                this.parsed.scripts[idx].code = responseText;
                if(i+1 == this.parsed.scripts.length) completed();
            }.bind(this);
            
            new Ajax(src, {
                'method':       'get',
                'onComplete':    onComplete
            }).request();
        }.bind(this);

        if(this.parsed.scripts.length > 0) {
            for(var i=0; i < this.parsed.scripts.length; i++) {
                if(this.parsed.scripts[i].src) {
                    getRemoteScript(this.parsed.scripts[i].src, i);
                }
                else if(i+1 == this.parsed.scripts.length) completed();
            }
        }
        else completed();
    },
    
    executeScripts: function() {
        var scripts = "";
        for(var i = 0; i < this.parsed.scripts.length; i++) {
            if(this.parsed.scripts[i].code) scripts = scripts + this.parsed.scripts[i].code;
        }
        eval(scripts);
    },
    
    applyStyles: function() {
        if(this.parsed.links) {
            this.parsed.links.each(function(links) {
                if(links.type.toLowerCase() = "text/css") new Asset.css(links.href);
            });
        }
        
        if(this.parsed.styles) {
            var styles = "";
            for(var i = 0; i < this.parsed.styles.length; i++) {
                if(this.parsed.styles[i].code) styles = styles + this.parsed.styles[i].code;
            }
            var stylesheet = new Element('style', {type:'text/css'}).injectInside(document.head);
            if(!!window.ie) document.styleSheets[document.styleSheets.length-1].cssText = styles;
            else stylesheet.appendText(styles);
        }
    },
    
    load: function() {
        this.request(this.data);
    }
});

Loader.implement(new Events, new Options);