/**
 * JsHttpRequest: JavaScript "AJAX" data loader.
 * (C) 2006 Dmitry Koterov, http://forum.dklab.ru/users/DmitryKoterov/
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * See http://www.gnu.org/copyleft/lesser.html
 *
 * Do not remove this comment if you want to use script!
 * Не удаляйте данный комментарий, если вы хотите использовать скрипт!
 *
 * This library tries to use XMLHttpRequest (if available), and on 
 * failure - use dynamically created <script> elements. Backend code
 * is the same for both cases. Library also supports file uploading;
 * in this case it uses FORM+IFRAME-based loading.
 *
 * @author Dmitry Koterov 
 * @version 4.14
 *
 * @Updated by Tretyak Sergey (aka Goshi ml11fin@inbox.ru) specialy for PowerPHP 
 * @version 4.2
 *
 * 4.2		27.05.10/goshi	fix clearing radio buttons symbols
 * 4.15		20.04.09/goshi	add checking for portal.vars.friendlyURL property
 * 4.14.3	25.02.09/goshi	remove bug with '?' symbol and now queryText
 * 4.14.2			remov bug with indexOf('?')
 * Added friendly URL queries for caching!
 *
 */

function JsHttpRequest() { this._construct() };
(function() { // to create local-scope variables
    var COUNT       = 0;
    var PENDING     = {};
    var CACHE       = {};

    // Called by server script on data load. Static.
    JsHttpRequest.dataReady = function(id, text, js) {
        var undef;
        var th = PENDING[id];
        delete PENDING[id];
        if (th) {
            delete th._xmlReq;
            if (th.caching && th.hash) CACHE[th.hash] = [text, js];
            th._dataReady(text, js);
        } else if (th !== false) {
            throw "JsHttpRequest.dataReady(): unknown pending id: " + id;
        }
    };

    // Simple interface for most popular use-case.
    JsHttpRequest.query = function(url, content, onready, nocache) {
        var req = new JsHttpRequest();
        req.caching = !nocache;
        req.onreadystatechange = function() {
            if (req.readyState == 4) {
                onready(req.responseJS, req.responseText);
            }
        };
        req.open(null, url, true);
        req.send(content);
    },
    
    JsHttpRequest.prototype = {
        // Standard properties.
        onreadystatechange: null,
        readyState:         0,
        responseText:       null,
        responseXML:        null,
        friendlyURL:	    true, // by default set this parameter to true
        status:             200,
        statusText:         "OK",
        // JavaScript response array/hash
        responseJS:         null,

        // Additional properties.
        session_name:       "PHPSESSID",  // set to SID cookie or GET parameter name
        caching:            false,        // need to use caching?
        loader:             null,         // loader to use ('form', 'script', 'xml'; null - autodetect)

        // Internals.
        _span:              null,
        _id:                null,
        _xmlReq:            null,
        _openArg:           null,
        _reqHeaders:        null,
        _maxUrlLen:         2000,

        dummy: function() {}, // empty constant function for ActiveX leak minimization

        abort: function() {
            if (this._xmlReq) {
                this._xmlReq.abort();
                this._xmlReq = null;
            }
            this._cleanupScript();
            this._changeReadyState(4, true); // 4 in IE & FF on abort() call; Opera does not change to 4.
        },
        
        /*
        * function make frindly URL from dynamic 
        * Goshi update
        */
        _parseFriendURL: function(url) {
               	
        	var queryText = url.split('?', 2)[1].split('&');

		var url = url.split('?', 2)[0];
		var tmpUrl = url.split('/');
		
      		var uLast = tmpUrl[tmpUrl.length - 1];
      		tmpUrl = tmpUrl.slice(0, tmpUrl.length - 1);
      		
      		for (var i in queryText){
      			if (queryText[i] instanceof Function) continue;
       			tmpUrl.push(queryText[i].split('=')[0], queryText[i].split('=')[1]);
       		}
       		       		
      		tmpUrl.push(uLast);
       		url = tmpUrl.join('/');
       		return url;
        
        },

        open: function(method, url, asyncFlag, username, password) {
            // Append SID to original URL.
            var sid = this._getSid();
            if (sid) {
            	if (this.friendlyURL && url.indexOf('?') != -1){
            		url = this._parseFriendURL(url);
            	}
	        
                url += (url.indexOf('?')>=0? '&' : '?') + this.session_name + "=" + this.escape(sid);
                
            }
            
            this._openArg = {
                method:     (method||'').toUpperCase(),
                url:        url,
                asyncFlag:  asyncFlag,
                username:   username != null? username : '',
                password:   password != null? password : ''
            };
            this._id = null;
            this._xmlReq = null;
            this._reqHeaders = [];
            this._changeReadyState(1, true); // compatibility with XMLHttpRequest
            return true;
        },
        
        send: function(content) {
            this._changeReadyState(1, true); // compatibility with XMLHttpRequest
            var url = this._openArg.url; 

            // Prepare to build QUERY_STRING from query hash.
            var queryText = [];
            var queryElem = [];
            if (!this._hash2query(content, null, queryText, queryElem)) return;
	
            var loader = (this.loader||'').toLowerCase();
            var method = this._openArg.method;
            var xmlReq = null;
            
            // Full URL if parameters are passed via GET.
            var qtext = queryText.join('&');
            if (qtext)
            	var fullGetUrl = url + (url.indexOf('?')>=0? '&' : '?') + queryText.join('&');
            else
            	var fullGetUrl = url + (url.indexOf('?')>=0? '&' : '');

            // parse URL
            if (this.friendlyURL && url.indexOf('?') != -1){
            	fullGetUrl = this._parseFriendURL(fullGetUrl);
            }
            
            //var id = (new Date().getTime()) + "" + COUNT++;
            /* 
            * now id - is a md5 hash value of the URL! 
            */
            var id = this.md5(fullGetUrl);
            
            if (queryElem.length && !loader) {
                // Always use form loader if we have at least one form element.
                loader = 'form';
            } else {
                // Try to obtain XML request object.
                xmlReq = this._obtainXmlReq(id, url);
            }
           
            // Solve hashcode BEFORE appending ID and check if cache is already present.
            this.hash = null;
            if (this.caching && !queryElem.length) {
                this.hash = fullGetUrl;
                if (CACHE[this.hash]) {
                    var c = CACHE[this.hash];
                    this._dataReady(c[0], c[1]);
                    return false;
                }
            }

            // Detect loader and method. (Yes, lots of code and conditions!)
            var canSetHeaders = xmlReq && (window.ActiveXObject || xmlReq.setRequestHeader); 
            if (!loader) {
                // Auto-detect loader.
                if (xmlReq) {
                    // Can use XMLHttpRequest.
                    loader = 'xml';
                    switch (method) {
                        case "POST":
                            if (!canSetHeaders) {
                                // Use POST method. Pass query in request body.
                                // Opera 8.01 does not support setRequestHeader, so no POST method.
                                loader = 'form';
                            }
                            break;
                        case "GET":
                            // Length of the query is checked later.
                            break;
                        default:
                            // Method is not set: auto-detect method.
                            if (canSetHeaders) {
                                method = 'POST';
                            } else {
                                if (fullGetUrl.length > this._maxUrlLen) {
                                    method = 'POST';
                                    loader = 'form';
                                } else {
                                    method = 'GET';
                                }
                            }
                    }
                } else {
                    // Cannot use XMLHttpRequest.
                    loader = 'script';
                    switch (method) {
                        case "POST":
                            loader = 'form';
                            break;
                        case "GET":
                            // Length of the query is checked later.
                            break;
                        default:
                            if (fullGetUrl.length > this._maxUrlLen) {
                                method = 'POST';
                                loader = 'form';
                            } else {
                                method = 'GET';
                            }
                    }
                }
            } else if (!method) {
                // Loader is pre-defined, but method is not set.
                switch (loader) {
                    case 'form':
                        method = 'POST';
                        break;
                    case 'script':
                        method = 'GET';
                        break;
                    default:
                        if (canSetHeaders) {
                            method = 'POST';
                        } else {
                            method = 'GET';
                        }
                }
            }

            // Correct GET URL.
            var requestBody = null;
            if (method == 'GET') {
                url = fullGetUrl;
                if (url.length > this._maxUrlLen) return this._error('Cannot use so long query (URL is ' + url.length + ' byte(s) length) with GET request.');
            } else if (method == 'POST') {
                requestBody = queryText.join('&');
            } else {
                return this._error('Unknown method: ' + method + '. Only GET and POST are supported.');
            }

            // Append loading ID to URL: a=aaa&b=bbb&<id>
            url = url + (url.indexOf('?')>=0? '&' : '?') + 'JsHttpRequest=' + id + '-' + loader;

            // Save loading script.
            PENDING[id] = this;

            // Send the request.
            switch (loader) {
                case 'xml':
                    // Use XMLHttpRequest.
                    if (!xmlReq) return this._error('Cannot use XMLHttpRequest or ActiveX loader: not supported');
                    if (method == "POST" && !canSetHeaders) return this._error('Cannot use XMLHttpRequest loader or ActiveX loader, POST method: headers setting is not supported');
                    if (queryElem.length) return this._error('Cannot use XMLHttpRequest loader: direct form elements using and uploading are not implemented');
                    this._xmlReq = xmlReq;
                    var a = this._openArg;
                    this._xmlReq.open(method, url, a.asyncFlag, a.username, a.password);
                    if (canSetHeaders) {
                        // Pass pending headers.
                        for (var i=0; i<this._reqHeaders.length; i++)
                            this._xmlReq.setRequestHeader(this._reqHeaders[i][0], this._reqHeaders[i][1]);
                        // Set non-default Content-type. We cannot use 
                        // "application/x-www-form-urlencoded" here, because 
                        // in PHP variable HTTP_RAW_POST_DATA is accessible only when 
                        // enctype is not default (e.g., "application/octet-stream" 
                        // is a good start). We parse POST data manually in backend 
                        // library code.
                        this._xmlReq.setRequestHeader('Content-Type', 'application/octet-stream');
                    }
                    // Send the request.
                    return this._xmlReq.send(requestBody);

                case 'script':
                    // Create <script> element and run it.
                    if (method != 'GET') return this._error('Cannot use SCRIPT loader: it supports only GET method');
                    if (queryElem.length) return this._error('Cannot use SCRIPT loader: direct form elements using and uploading are not implemented');
                    this._obtainScript(id, url);
                    return true;

                case 'form':
                    // Create & submit FORM.
                    if (!this._obtainForm(id, url, method, queryText, queryElem)) return null;
                    return true;

                default:
                    return this._error('Unknown loader: ' + loader);
            }
        },

        getAllResponseHeaders: function() {
            if (this._xmlReq) return this._xmlReq.getAllResponseHeaders();
            return '';
        },
            
        getResponseHeader: function(label) {
            if (this._xmlReq) return this._xmlReq.getResponseHeader(label);
            return '';
        },

        setRequestHeader: function(label, value) {
            // Collect headers.
            this._reqHeaders[this._reqHeaders.length] = [label, value];
        },


        //
        // Internal functions.
        //

        // Constructor.
        _construct: function() {
         if (typeof portal != "undefined" && portal.vars)
         	this.friendlyURL = portal.vars.friendlyURL;
         else if (window['JsFriendly'] !== undefined)
           	this.friendlyURL = window['JsFriendly'];
         },

        // Do all work when data is ready.
        _dataReady: function(text, js) { with (this) {
            if (text !== null || js !== null) {
                status = 4;
                responseText = responseXML = text;
                responseJS = js;
            } else {
                status = 500;
                responseText = responseXML = responseJS = null;
            }
            _changeReadyState(2);
            _changeReadyState(3);
            _changeReadyState(4);
            _cleanupScript();
        }},

        // Called on error.
        _error: function(msg) {
            throw (window.Error? new Error(msg) : msg);
        },

        // Create new XMLHttpRequest object.
        _obtainXmlReq: function(id, url) {
            // If url.domain specified and differ from current, cannot use XMLHttpRequest!
            // XMLHttpRequest (and MS ActiveX'es) cannot work with different domains.
            var p = url.match(new RegExp('^[a-z]+://(.*)', 'i'));
            if (p) {
                var curHost = document.location.host.toLowerCase();
                if (p[1].substring(0, curHost.length).toLowerCase() == curHost) {
                    url = p[1].substring(curHost.length, p[1].length);
                } else {
                    return null;
                }
            }
            
            // Try to use built-in loaders.
            var req = null;
            if (window.XMLHttpRequest) {
                try { req = new XMLHttpRequest() } catch(e) {}
            } else if (window.ActiveXObject) {
                try { req = new ActiveXObject("Microsoft.XMLHTTP") } catch(e) {}
                if (!req) try { req = new ActiveXObject("Msxml2.XMLHTTP") } catch (e) {}
            }
            if (req) {
                var th = this;
                req.onreadystatechange = function() { 
                    if (req.readyState == 4) {
                        // Avoid memory leak by removing closure.
                        req.onreadystatechange = th.dummy;
                        th.status = null;
                        try { 
                            // In case of abort() call, req.status is unavailable and generates exception.
                            // But req.readyState equals to 4 in this case. Stupid behaviour. :-(
                            th.status = req.status;
                            th.responseText = req.responseText;
                        } catch (e) {}
                        if (!th.status) return;
                        var funcRequestBody = null;
                        try {
                            // Prepare generator function & catch syntax errors on this stage.
                            eval('funcRequestBody = function() {\n' + th.responseText + '\n}');
                        } catch (e) {
                            return th._error("JavaScript code generated by backend is invalid!\n" + th.responseText);
                        }
                        // Call associated dataReady() outside try-catch block 
                        // to pass excaptions in onreadystatechange in usual manner.
                        funcRequestBody();
                    }
                };
                this._id = id;
            }
            return req;
        },

        // Create new script element and start loading.
        _obtainScript: function(id, href) { with (document) {
            // Oh shit! Damned stupid fucked Opera 7.23 does not allow to create SCRIPT 
            // element over createElement (in HEAD or BODY section or in nested SPAN - 
            // no matter): it is created deadly, and does not respons on href assignment.
            // So - always create SPAN.
            var span = createElement('SPAN');
            span.style.display = 'none';
            body.insertBefore(span, body.lastChild);
            span.innerHTML = 'Text for stupid IE.<s'+'cript></' + 'script>';
            setTimeout(function() {
                var s = span.getElementsByTagName('script')[0];
                s.language = 'JavaScript';
                if (s.setAttribute) s.setAttribute('src', href); else s.src = href;
            }, 10);
            this._id = id;
            this._span = span;
        }},

        // Create & submit form.
        _obtainForm: function(id, url, method, queryText, queryElem) {
            // In case of GET method - split real query string.
            if (method == 'GET') {
                queryText = url.split('?', 2)[1].split('&');
                url = url.split('?', 2)[0];
            }

            // Create invisible IFRAME with temporary form (form is used on empty queryElem).
            var div = document.createElement('DIV');
            div.id = 'jshr_d_' + id;
            div.style.position = 'absolute';
            div.style.visibility = 'hidden';
            div.innerHTML = 
                '<form enctype="multipart/form-data"></form>' + // stupid IE, MUST use innerHTML assignment :-(
                '<iframe src="javascript:\'\'" name="jshr_i_' + id + '" style="width:0px; height:0px; overflow:hidden; border:none"></iframe>';
            var form = div.getElementsByTagName('FORM')[0];
            var iframe = div.getElementsByTagName('IFRAME')[0];

            // Check if all form elements belong to same form.
            if (queryElem.length) {
                // If we have at least one form element, we use its form as POST container.
                form = queryElem[0][1].form;
                var foundFile = false;
                for (var i = 0; i < queryElem.length; i++) {
                    var e = queryElem[i][1];
                    if (!e.form) {
                        return this._error('Element "' + e.name + '" do not belongs to any form!');
                    }
                    if (e.form != form) {
                        return this._error('Element "' + e.name + '" belongs to different form. All elements must belong to the same form!');
                    }
                    foundFile = foundFile || (e.tagName.toLowerCase() == 'input' && (e.type||'').toLowerCase() == 'file');
                }
                var et = "multipart/form-data";
                if (form.enctype != et && foundFile) {
                    return this._error('Attribute "enctype" of elements\' form must be "' + et + '" (for IE), "' + form.enctype + '" given.');
                }
            }

            // Temporary disable ALL form elements in 'form' (including custom!).
            // THIS IS THE MAIN BUG IN ELEMTNS CREATING!
            // RADIO BUTTONS LOST THEIR STATE 
            /*for (var i = 0; i < form.elements.length; i++) {
                var e = form.elements[i];
                if (e.name != null) {
                    e.jshrSaveName = e.name;
                    e.name = '';
                }
            }*/

            // Insert hidden fields to the form.
            var tmpE = [];
            for (var i=0; i<queryText.length; i++) {
                var pair = queryText[i].split('=', 2);
                var e = document.createElement('INPUT');
                e.type = 'hidden';
                e.name = unescape(pair[0]);
                e.value = pair[1] != null? unescape(pair[1]) : '';
                form.appendChild(e);
                tmpE[tmpE.length] = e;
            }

            // Enable custom form elements back & change their names.
            for (var i = 0; i < queryElem.length; i++) queryElem[i][1].name = queryElem[i][0];

            // Insert generated form inside the document.
            // Be careful: don't forget to close FORM container in document body!
            document.body.insertBefore(div, document.body.lastChild);
            this._span = div;

            // Temporary modify form attributes, submit form, restore attributes back.
            var sv = {};
            sv.enctype  = form.enctype;  form.enctype = "multipart/form-data";
            sv.action   = form.action;   form.action = url;
            sv.method   = form.method;   form.method = method;
            sv.target   = form.target;   form.target = iframe.name;
            sv.onsubmit = form.onsubmit; form.onsubmit = null;
            form.submit();
            for (var i in sv) form[i] = sv[i];
            
            // Remove generated temporary hidden elements from form.
            for (var i = 0; i < tmpE.length; i++) tmpE[i].parentNode.removeChild(tmpE[i]);

            // Enable all disabled elements back.
            for (var i = 0; i < form.elements.length; i++) {
                var e = form.elements[i];
                if (e.jshrSaveName != null) {
                    e.name = e.jshrSaveName;
                    e.jshrSaveName = null;
                }
            }
        },

        // Remove last used script element (clean memory).
        _cleanupScript: function() {
            var span = this._span;
            if (span) {
                this._span = null;
                setTimeout(function() {
                    // without setTimeout - crash in IE 5.0!
                    span.parentNode.removeChild(span);
                }, 50);
            }
            if (this._id) {
                // Mark this loading as aborted.
                PENDING[this._id] = false;
            }
            return false;
        },

        // Convert hash to QUERY_STRING.
        // If next value is scalar or hash, push it to queryText.
        // If next value is form element, push [name, element] to queryElem.
        _hash2query: function(content, prefix, queryText, queryElem) {
            if (prefix == null) prefix = "";
            if (content instanceof Object) {
                for (var k in content) {
                    var v = content[k];
                    if (v instanceof Function) continue;

                    var curPrefix = prefix? prefix+'['+this.escape(k)+']' : this.escape(k);
                    if (this._isFormElement(v)) {
                        var tn = v.tagName.toLowerCase();
                        if (tn == 'form') {
                            // This is FORM itself. Add all its elements.
                            for (var i=0; i<v.elements.length; i++) {
                                var e = v.elements[i];
                                if (e.name) queryElem[queryElem.length] = [e.name, e];
                            }
                        } else if (tn == 'input' || tn == 'textarea' || tn == 'select') {
                            // This is a single form elemenent.
                            queryElem[queryElem.length] = [curPrefix, v];
                        } else {
                            return this._error('Invalid FORM element detected: name=' + (e.name||'') + ', tag=' + e.tagName);
                        }
                    } else if (v instanceof Object) {
                        this._hash2query(v, curPrefix, queryText, queryElem);
                    } else {
                        // We MUST skip NULL values, because there is no method
                        // to pass NULL's via GET or POST request in PHP.
                        if (v === null) continue;
                        queryText[queryText.length] = curPrefix + "=" + this.escape('' + v);
                    }
                }
            } else {
                queryText = [content];
            }
            return true;
        },

        // Return true if e is any form element of FORM itself.
        _isFormElement: function(e) {
            // Fast & dirty method.
            return e && e.ownerDocument && e.parentNode && e.parentNode.appendChild && e.tagName;
        },

        // Return value of SID based on QUERY_STRING or cookie
        // (PHP compatible sessions).
        _getSid: function() {
            var m = document.location.search.match(new RegExp('[&?]'+this.session_name+'=([^&?]*)'));
            var sid = null;
            if (m) {
                sid = m[1];
            } else {
                var m = document.cookie.match(new RegExp('(;|^)\\s*'+this.session_name+'=([^;]*)'));
                if (m) sid = m[2];
            }
            return sid;
        },

        // Change current readyState and call trigger method.
        _changeReadyState: function(s, reset) { with (this) {
            if (reset) {
                status = statusText = responseJS = null;
                responseText = '';
            }
            readyState = s;
            if (onreadystatechange) onreadystatechange();
        }},

        // Stupid JS escape() does not quote '+'.
        escape: function(s) {
            return escape(s).replace(new RegExp('\\+','g'), '%2B');
        },
        
        
        /*
        * new fucntion of md5
        * added by Goshi 18.06.2008
        */
        md5: function(str) {
	    // http://kevin.vanzonneveld.net
	    // +   original by: Webtoolkit.info (http://www.webtoolkit.info/)
	    // + namespaced by: Michael White (http://crestidg.com)
	    // -    depends on: utf8_encode
	    // *     example 1: md5('Kevin van Zonneveld');
	    // *     returns 1: '6e658d4bfcb59cc13f96c14450ac40b9'
	 
	    var RotateLeft = function(lValue, iShiftBits) {
	            return (lValue<<iShiftBits) | (lValue>>>(32-iShiftBits));
	        };
	 
	    var AddUnsigned = function(lX,lY) {
	            var lX4,lY4,lX8,lY8,lResult;
	            lX8 = (lX & 0x80000000);
	            lY8 = (lY & 0x80000000);
	            lX4 = (lX & 0x40000000);
	            lY4 = (lY & 0x40000000);
	            lResult = (lX & 0x3FFFFFFF)+(lY & 0x3FFFFFFF);
	            if (lX4 & lY4) {
	                return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
	            }
	            if (lX4 | lY4) {
	                if (lResult & 0x40000000) {
	                    return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
	                } else {
	                    return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
	                }
	            } else {
	                return (lResult ^ lX8 ^ lY8);
	            }
	        };
	 
	    var F = function(x,y,z) { return (x & y) | ((~x) & z); };
	    var G = function(x,y,z) { return (x & z) | (y & (~z)); };
	    var H = function(x,y,z) { return (x ^ y ^ z); };
	    var I = function(x,y,z) { return (y ^ (x | (~z))); };
	 
	    var FF = function(a,b,c,d,x,s,ac) {
	            a = AddUnsigned(a, AddUnsigned(AddUnsigned(F(b, c, d), x), ac));
	            return AddUnsigned(RotateLeft(a, s), b);
	        };
	 
	    var GG = function(a,b,c,d,x,s,ac) {
	            a = AddUnsigned(a, AddUnsigned(AddUnsigned(G(b, c, d), x), ac));
	            return AddUnsigned(RotateLeft(a, s), b);
	        };
	 
	    var HH = function(a,b,c,d,x,s,ac) {
	            a = AddUnsigned(a, AddUnsigned(AddUnsigned(H(b, c, d), x), ac));
	            return AddUnsigned(RotateLeft(a, s), b);
	        };
	 
	    var II = function(a,b,c,d,x,s,ac) {
	            a = AddUnsigned(a, AddUnsigned(AddUnsigned(I(b, c, d), x), ac));
	            return AddUnsigned(RotateLeft(a, s), b);
	        };
	 
	    var ConvertToWordArray = function(str) {
	            var lWordCount;
	            var lMessageLength = str.length;
	            var lNumberOfWords_temp1=lMessageLength + 8;
	            var lNumberOfWords_temp2=(lNumberOfWords_temp1-(lNumberOfWords_temp1 % 64))/64;
	            var lNumberOfWords = (lNumberOfWords_temp2+1)*16;
	            var lWordArray=Array(lNumberOfWords-1);
	            var lBytePosition = 0;
	            var lByteCount = 0;
	            while ( lByteCount < lMessageLength ) {
	                lWordCount = (lByteCount-(lByteCount % 4))/4;
	                lBytePosition = (lByteCount % 4)*8;
	                lWordArray[lWordCount] = (lWordArray[lWordCount] | (str.charCodeAt(lByteCount)<<lBytePosition));
	                lByteCount++;
	            }
	            lWordCount = (lByteCount-(lByteCount % 4))/4;
	            lBytePosition = (lByteCount % 4)*8;
	            lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80<<lBytePosition);
	            lWordArray[lNumberOfWords-2] = lMessageLength<<3;
	            lWordArray[lNumberOfWords-1] = lMessageLength>>>29;
	            return lWordArray;
	        };
	 
	    var WordToHex = function(lValue) {
	            var WordToHexValue="",WordToHexValue_temp="",lByte,lCount;
	            for (lCount = 0;lCount<=3;lCount++) {
	                lByte = (lValue>>>(lCount*8)) & 255;
	                WordToHexValue_temp = "0" + lByte.toString(16);
	                WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length-2,2);
	            }
	            return WordToHexValue;
	        };
	 
	    var x=Array();
	    var k,AA,BB,CC,DD,a,b,c,d;
	    var S11=7, S12=12, S13=17, S14=22;
	    var S21=5, S22=9 , S23=14, S24=20;
	    var S31=4, S32=11, S33=16, S34=23;
	    var S41=6, S42=10, S43=15, S44=21;
	 
	    str = this.utf8_encode(str);
	    x = ConvertToWordArray(str);
	    a = 0x67452301; b = 0xEFCDAB89; c = 0x98BADCFE; d = 0x10325476;
	 
	    for (k=0;k<x.length;k+=16) {
	        AA=a; BB=b; CC=c; DD=d;
	        a=FF(a,b,c,d,x[k+0], S11,0xD76AA478);
	        d=FF(d,a,b,c,x[k+1], S12,0xE8C7B756);
	        c=FF(c,d,a,b,x[k+2], S13,0x242070DB);
	        b=FF(b,c,d,a,x[k+3], S14,0xC1BDCEEE);
	        a=FF(a,b,c,d,x[k+4], S11,0xF57C0FAF);
	        d=FF(d,a,b,c,x[k+5], S12,0x4787C62A);
	        c=FF(c,d,a,b,x[k+6], S13,0xA8304613);
	        b=FF(b,c,d,a,x[k+7], S14,0xFD469501);
	        a=FF(a,b,c,d,x[k+8], S11,0x698098D8);
	        d=FF(d,a,b,c,x[k+9], S12,0x8B44F7AF);
	        c=FF(c,d,a,b,x[k+10],S13,0xFFFF5BB1);
	        b=FF(b,c,d,a,x[k+11],S14,0x895CD7BE);
	        a=FF(a,b,c,d,x[k+12],S11,0x6B901122);
	        d=FF(d,a,b,c,x[k+13],S12,0xFD987193);
	        c=FF(c,d,a,b,x[k+14],S13,0xA679438E);
	        b=FF(b,c,d,a,x[k+15],S14,0x49B40821);
	        a=GG(a,b,c,d,x[k+1], S21,0xF61E2562);
	        d=GG(d,a,b,c,x[k+6], S22,0xC040B340);
	        c=GG(c,d,a,b,x[k+11],S23,0x265E5A51);
	        b=GG(b,c,d,a,x[k+0], S24,0xE9B6C7AA);
	        a=GG(a,b,c,d,x[k+5], S21,0xD62F105D);
	        d=GG(d,a,b,c,x[k+10],S22,0x2441453);
	        c=GG(c,d,a,b,x[k+15],S23,0xD8A1E681);
	        b=GG(b,c,d,a,x[k+4], S24,0xE7D3FBC8);
	        a=GG(a,b,c,d,x[k+9], S21,0x21E1CDE6);
	        d=GG(d,a,b,c,x[k+14],S22,0xC33707D6);
	        c=GG(c,d,a,b,x[k+3], S23,0xF4D50D87);
	        b=GG(b,c,d,a,x[k+8], S24,0x455A14ED);
	        a=GG(a,b,c,d,x[k+13],S21,0xA9E3E905);
	        d=GG(d,a,b,c,x[k+2], S22,0xFCEFA3F8);
	        c=GG(c,d,a,b,x[k+7], S23,0x676F02D9);
	        b=GG(b,c,d,a,x[k+12],S24,0x8D2A4C8A);
	        a=HH(a,b,c,d,x[k+5], S31,0xFFFA3942);
	        d=HH(d,a,b,c,x[k+8], S32,0x8771F681);
	        c=HH(c,d,a,b,x[k+11],S33,0x6D9D6122);
	        b=HH(b,c,d,a,x[k+14],S34,0xFDE5380C);
	        a=HH(a,b,c,d,x[k+1], S31,0xA4BEEA44);
	        d=HH(d,a,b,c,x[k+4], S32,0x4BDECFA9);
	        c=HH(c,d,a,b,x[k+7], S33,0xF6BB4B60);
	        b=HH(b,c,d,a,x[k+10],S34,0xBEBFBC70);
	        a=HH(a,b,c,d,x[k+13],S31,0x289B7EC6);
	        d=HH(d,a,b,c,x[k+0], S32,0xEAA127FA);
	        c=HH(c,d,a,b,x[k+3], S33,0xD4EF3085);
	        b=HH(b,c,d,a,x[k+6], S34,0x4881D05);
	        a=HH(a,b,c,d,x[k+9], S31,0xD9D4D039);
	        d=HH(d,a,b,c,x[k+12],S32,0xE6DB99E5);
	        c=HH(c,d,a,b,x[k+15],S33,0x1FA27CF8);
	        b=HH(b,c,d,a,x[k+2], S34,0xC4AC5665);
	        a=II(a,b,c,d,x[k+0], S41,0xF4292244);
	        d=II(d,a,b,c,x[k+7], S42,0x432AFF97);
	        c=II(c,d,a,b,x[k+14],S43,0xAB9423A7);
	        b=II(b,c,d,a,x[k+5], S44,0xFC93A039);
	        a=II(a,b,c,d,x[k+12],S41,0x655B59C3);
	        d=II(d,a,b,c,x[k+3], S42,0x8F0CCC92);
	        c=II(c,d,a,b,x[k+10],S43,0xFFEFF47D);
	        b=II(b,c,d,a,x[k+1], S44,0x85845DD1);
	        a=II(a,b,c,d,x[k+8], S41,0x6FA87E4F);
	        d=II(d,a,b,c,x[k+15],S42,0xFE2CE6E0);
	        c=II(c,d,a,b,x[k+6], S43,0xA3014314);
	        b=II(b,c,d,a,x[k+13],S44,0x4E0811A1);
	        a=II(a,b,c,d,x[k+4], S41,0xF7537E82);
	        d=II(d,a,b,c,x[k+11],S42,0xBD3AF235);
	        c=II(c,d,a,b,x[k+2], S43,0x2AD7D2BB);
	        b=II(b,c,d,a,x[k+9], S44,0xEB86D391);
	        a=AddUnsigned(a,AA);
	        b=AddUnsigned(b,BB);
	        c=AddUnsigned(c,CC);
	        d=AddUnsigned(d,DD);
	    }
	 
	    var temp = WordToHex(a)+WordToHex(b)+WordToHex(c)+WordToHex(d);
	 
	    return temp.toLowerCase();
	},
	
	
	utf8_encode: function(str_data) {
	    // http://kevin.vanzonneveld.net
	    // +   original by: Webtoolkit.info (http://www.webtoolkit.info/)
	    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)        
	    // *     example 1: utf8_encode('Kevin van Zonneveld');
	    // *     returns 1: 'Kevin van Zonneveld'
	 
	    str_data = str_data.replace(/\r\n/g,"\n");
	    var tmp_arr = [], ac = 0;
	 
	    for (var n = 0; n < str_data.length; n++) {
	        var c = str_data.charCodeAt(n);
	        if (c < 128) {
	            tmp_arr[ac++] = String.fromCharCode(c);
	        } else if((c > 127) && (c < 2048)) {
	            tmp_arr[ac++] = String.fromCharCode((c >> 6) | 192);
	            tmp_arr[ac++] = String.fromCharCode((c & 63) | 128);
	        } else {
	            tmp_arr[ac++] = String.fromCharCode((c >> 12) | 224);
	            tmp_arr[ac++] = String.fromCharCode(((c >> 6) & 63) | 128);
	            tmp_arr[ac++] = String.fromCharCode((c & 63) | 128);
	        }
	    }
	    
	    return tmp_arr.join('');
	}
        
        
    }
})();

