﻿/// <reference name="MicrosoftAjax.js"/>

Type.registerNamespace("Visiware.JS");

Visiware.JS.XMLControl = function(element) {
    /// <summary>
    /// A component that loads and renders XML using XSLT on the client,
    /// and adds a pager at the bottom
    /// </summary>
    Visiware.JS.XMLControl.initializeBase(this, [element]);
}

Visiware.JS.XMLControl.prototype = {
    // Data Members
    m_xml: null,
    m_xslt: null,
    m_xsltUrl: null,
    m_xmlUrl: null, // This must be a RESTful web service that supports paging
    m_reloadInterval: null,
    m_interval: null,
    m_timeoutID: null,
    m_lastMod: null,
    m_currentPageIndex: 0,
    m_progressHTML: '',
    m_isRefreshCache: false,
    
    get_xml: function() {
        /// <value>Gets or sets the XML data for the control. </value>
        return this.m_xml;
    },
    set_xml: function(value) {
        if (this.m_xml !== value) {
            this.m_xml = value;
            this.raisePropertyChanged('xml');
        }
        if (this.m_xml && this.m_xslt) {
            this._render();
        }
    },

    get_progressHTML: function() {
        return this.m_progressHTML;
    },
    set_progressHTML: function(value) {
        if (this.m_progressHTML !== value) {
            this.m_progressHTML = value;
            this.raisePropertyChanged('progressHTML');
        }
    },

    get_xslt: function() {
        /// <value>Gets or sets the XSLT for the control.</value>
        return this.m_xslt;
    },
    set_xslt: function(value) {
        if (this.m_xslt !== value) {
            this.m_xslt = value;
            this.raisePropertyChanged('xslt');
        }
        if (this.m_xml && this.m_xslt) {
            this._render();
        }
    },

    get_xsltUrl: function() {
        /// <value>The URL to load the XSLT from.</value>
        return this.m_xsltUrl;
    },
    set_xsltUrl: function(value) {
        if (this.m_xsltUrl !== value) {
            this.m_xsltUrl = value;
            this.raisePropertyChanged('xsltUrl');
            if (this.m_xsltUrl && !this.get_isUpdating()) {
                this._loadXslt(this.m_xsltUrl);
            }
        }
    },

    get_xmlUrl: function() {
        /// <value>The URL to load the data from.</value>
        return this.m_xmlUrl;
    },
    set_xmlUrl: function(value) {
        if (this.m_xmlUrl !== value) {
            this.m_lastMod = null;
            this.m_xmlUrl = value;
            this.raisePropertyChanged('xmlUrl');
            if (this.m_xmlUrl && !this.get_isUpdating()) {
                this._loadXml(this.m_xmlUrl);
            }
        }
    },

    get_reloadInterval: function() {
        /// <value>The interval at which we'll reload the control.</value>
        return this.m_reloadInterval;
    },
    set_reloadInterval: function(value) {
        if (this.m_reloadInterval !== value) {
            if (value != 0 && value < 99) {
                throw Error.argumentOutOfRange('reloadInterval',
                    value, 'The reload interval must be 0 or over 100 milliseconds');
            }
            this.m_reloadInterval = value;
            this.raisePropertyChanged('reloadInterval');
        }
    },

    get_isRefreshCache: function() {
        /// <value>The interval at which we'll reload the control.</value>
        return this.m_isRefreshCache;
    },
    set_isRefreshCache: function(value) {
        if (this.m_isRefreshCache !== value) {
            this.m_isRefreshCache = value;
            this.raisePropertyChanged('isRefreshCache');
        }
    },
    
    reload: function() {
        if (this.m_timeoutID) {
            window.clearTimeout(this.m_timeoutID);
            this._reload();
        }
    },

    _reload: function() {
        var xmlUrl = this.get_xmlUrl();
        if (xmlUrl != null) {
            this._loadXml(xmlUrl);
        }
    },

    // Events
    add_xmlLoaded: function(handler) {
        /// <value>Bind and unbind to the xmlLoaded event. </value>
        this.get_events().addHandler('xmlLoaded', handler);
    },
    remove_xmlLoaded: function(handler) {
        this.get_events().removeHandler('xmlLoaded', handler);
    },

    add_xsltLoaded: function(handler) {
        /// <value>Bind an dunbind to the xsltLoaded event.<value>
        this.get_events().addHandler('xsltLoaded', handler);
    },
    remove_xsltLoaded: function(handler) {
        this.get_events().removeHandler('xsltLoaded', handler);
    },

    add_render: function(handler) {
        /// <value>Bind and unbind to the render event.</value>
        this.get_events().addHandler('render', handler);
    },
    remove_render: function(handler) {
        this.get_events().removeHandler('render', handler);
    },

    add_prerender: function(handler) {
        /// <value> Bind and unbind to the prerender event.</value>
        this.get_events().addHandler('prerender', handler);
    },
    remove_prerender: function(handler) {
        this.get_events().removeHandler('prerender', handler);
    },

    add_error: function(handler) {
        /// <value>Bind and unbind to the error event.</value>
        this.get_events().addHandler('error', handler);
    },
    remove_error: function(handler) {
        this.get_events().removeHandler('error', handler);
    },

    initialize: function() {
        Visiware.JS.XMLControl.callBaseMethod(this, 'initialize');

        if (this.m_xsltUrl && !this.get_isUpdating()) {
            this._loadXslt(this.m_xsltUrl);
        }
        if (this.m_xmlUrl && !this.get_isUpdating()) {
            this._loadXml(this.m_xmlUrl);
        }
    },

    _loadXml: function(url) {
        var modifiedSince = null;
        
        if (this.m_timeoutID) {
            window.clearTimeout(this.m_timeoutID);
        }
        
        if (this.m_isRefreshCache || this.m_timeoutID){
            modifiedSince = new Date(0);
        }
        this.xmlUrl = url;
        if (url == null || url == '') {
            return;
        }
        var request = new Sys.Net.WebRequest();
        var context = new Object();
        request.add_completed(Function.createDelegate(this, this._loadXmlComplete));
        request.set_userContext(context);
        request.set_url(url);

        // If we use a timer then we must enforce the refreshing of data
        if (modifiedSince != null) {
            request.get_headers()["If-Modified-Since"] = modifiedSince;
        }

        request.invoke();
    },

    _loadXmlComplete: function(response) {
        if (response == null) {
            response = new Sys.Net.WebRequestExecutor();
        }
        var context = response.get_webRequest().get_userContext();
        var status = response.get_statusCode();
        var url = response.get_webRequest().get_url();
        var xml;
        if (status == 200) {
            try {
                var lastMod = response.getResponseHeader('Last-Modified');
                if (lastMod && lastMod != '' && this.m_lastMod == lastMod) {
                    this.queueReload();
                    return;
                }
                else {
                    this.m_lastMod = lastMod;
                }

                xml = response.get_xml();
                if (xml == null) {
                    xml = response.get_responseData();
                }
                if (!xml) {
                    throw Error.create('We could not load the XML data at the URL ' + url);
                }
                var xmlLoadedHandler = this.get_events().getHandler('xmlLoaded');
                if (xmlLoadedHandler) {
                    xmlLoadedHandler(this, xml);
                }
                this.set_xml(xml);
            }
            catch (e) {
                Sys.Debug.fail('Could not process callback method.');
            }
        }
        // Not modified, not handled by the browser
        else if (status == 304) {
            this.queueReload();
        }
        // Process the status. Could be 401, 404 (not found), 410 (gone)
        else {
            var statusText = response.get_statusText();
            Sys.Debug.trace(String.format('ERROR: {0} replied "{1}" ({2}).',
                url, statusText, status));
            var errorHandler = this.get_events().getHandler('error');

            // Default error handler
            if (!errorHandler) {
                switch (status) {
                    // HttpStatusCode.Gone                                      
                    case 410:
                        alert('Content has been removed');
                        break;
                    // HttpStatusCode.NotFound                                      
                    case 404:
                        alert('Could not find resource');
                        break;
                    default:
                        alert(String.format('ERROR: {0} replied "{1}" ({2}).',
                            url, statusText, status));
                }
            }
            else {
                errorHandler(response, Sys.EventArgs.Empty);
            }
        }
    },

    _loadXslt: function(url) {
        this.xslUrl = url;
        if (url == null || url == '') {
            return;
        }
        var request = new Sys.Net.WebRequest();
        Sys.Debug.trace("XSLT Load: " + url);
        request.add_completed(Function.createDelegate(this,
            this._loadXsltComplete));
        request.set_url(url);
        request.invoke();
    },

    _loadXsltComplete: function(response) {
        var context = response.get_webRequest().get_userContext();
        var status = response.get_statusCode();
        var xml;
        if (status == 200) {
            try {
                xml = response.get_xml();
                if (xml == null) {
                    xml = response.get_responseData();
                }
                var xsltLoadedHandler = this.get_events().getHandler('xsltLoaded');
                if (xsltLoadedHandler) {
                    xsltLoadedHandler(this, Sys.EventArgs.Empty);
                }
                if (xml) {
                    this.set_xslt(xml);
                }
            }
            catch (e) {
                var errorHandler = this.get_events().getHandler('error');
                if (errorHandler) {
                    errorHandler(response, Sys.EventArgs.Empty);
                }
                else {
                    Sys.Debug.trace('Could not process callback method.');
                }
            }
        }
    },

    _render: function() {
        var control = this.get_element();
        if (control == null || this.m_xml == null || this.m_xslt == null) {
            return;
        }
        var prerenderHandler = this.get_events().getHandler('prerender');
        if (prerenderHandler) {
            prerenderHandler(this, Sys.EventArgs.Empty);
        }
        this._xmlTransform(this.m_xml, this.m_xslt, control);
        this._rendered = true;
        var renderHandler = this.get_events().getHandler('render');
        if (renderHandler) {
            renderHandler(this, Sys.EventArgs.Empty);
        }
        this.queueReload();
    },

    queueReload: function() {
        if (this.m_timeoutID) {
            window.clearTimeout(this.m_timeoutID);
        }

        // Only queue a reload if we have a valid reload interval (over 100ms)
        if (this.m_reloadInterval && this.m_reloadInterval > 100) {
            this.m_timeoutID = window.setTimeout(
                String.format("Visiware.JS.XMLControl.Refresh('{0}')",
                this.get_element().id),
                this.m_reloadInterval);
        }
    },

    dispose: function() {
        /// <summary>Release the resource before control is disposed.</summary>
        var element = this.get_element();
        if (element) {
            try {
                $clearHandlers(this.get_element());
            }
            catch (e) { }
        }
        Visiware.JS.XMLControl.callBaseMethod(this, 'dispose');
    },

    _xmlTransform: function(xml, xsl, control, decode) {
        var decode;
        if (decode == null) {
            decode = true;
        }

        // IE - using MSXML
        if (!window.XSLTProcessor) {
            control.innerHTML = xml.transformNode(xsl);
        }
        // Mozilla
        else {
            var processor = new XSLTProcessor();
            processor.importStylesheet(xsl);

            var content = processor.transformToFragment(xml, document);
            if (decode) {
                var div = document.createElement('div');
                div.appendChild(content);
                control.innerHTML = this._htmlDecode(div.innerHTML);
            }
            else {
                control.appendChild(content);
            }
        }
    },

    _htmlDecode: function(end) {
        return end.replace(/&quot;/gi, String.fromCharCode(0x0022))
            .replace(/&amp;/gi, String.fromCharCode(0x0026))
            .replace(/&lt;/gi, String.fromCharCode(0x003c))
            .replace(/&gt;/gi, String.fromCharCode(0x03e));
    },

    get_currentPageIndex: function() { return this.m_currentPageIndex; },
    set_currentPageIndex: function(value) { this.m_currentPageIndex = value; }
}

Visiware.JS.XMLControl.registerClass('Visiware.JS.XMLControl', Sys.UI.Control);

Visiware.JS.XMLControl.Refresh = function(elementID) {
    var xmlControl = $find(elementID);
    if (xmlControl && xmlControl.reload) {
        xmlControl.reload();
    }
}

if (typeof(Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();