function ajaxPageNamespace() {

    var LINK_CLASS = 'ajax-page';
    var url;
    var drawer;
    var request;

    function go(e) {
        var target = getTarget(e);
        if (isActionable(target)) {
            createDrawer();
            if (!request) {
                request = getTransport();
            } else {
                request.abort();
            }
            if (request) {
                // show loading here?
                if (!drawer.moving) {
                    url = target.href;
                    getPage();
                }
                return false;
            }
        }
        return true;
    }

    function isActionable(target) {
        return (target.nodeType == 1 && 'a' == target.tagName.toLowerCase() && hasClass(target, LINK_CLASS));
    }

    function createDrawer() {
        if (!drawer) {
            document.body.innerHTML = '<div id="ajax-page-drawer"><div>' + document.body.innerHTML + '</div></div>';
            drawer = new Drawer('ajax-page-drawer', 0.3, Drawer.BOTTOM, true);
        }
    }

    function getPage() {
        try {
            request.open('GET', url);
            request.onreadystatechange = handleResponse;
            request.send(null);
        } catch (ex) {
            window.location = url;
        }
    }

    function handleResponse() {
        if (request.readyState != 4)
            return;
        if (request.status < 200 || request.status > 307) {
            window.location = url;
            return;
        }
        var bodyMatch = request.responseText.match(/<body([^>]*)>([\s\S]*)<\/body>/i);
        if (bodyMatch == null) {
            window.location = url;
            return;
        }
        drawer.onend = function() {
            this.inner.innerHTML = bodyMatch[2];

            // Hack! I would prefer to use the DOM here if only IE would recognize responseXML
            // for documents that are not sent as text/xml. :(
            document.body.className = bodyMatch[1].getAttr('class');
            document.body.id = bodyMatch[1].getAttr('id');

            var titleMatch = request.responseText.match(/<title>([\s\S]*?)<\/title>/i);
            if (titleMatch) document.title = titleMatch[1];

            if (typeof window.onload == 'function') window.onload();

            drawer.onend = null;
            drawer.activate();
        }
        // hide loading here?
        window.scrollTo(0,0);
        drawer.activate();
    }

    document.onclick = go;

    // utility functions

    function getTransport() {
        if (typeof XMLHttpRequest != 'undefined') {
            return new XMLHttpRequest();
        }
        var prefixes = ['Microsoft', 'MSXML2', 'MSXML3', 'MSXML4', 'MSXML5'];
        for (var i = 0; i < prefixes.length; i++) {
            try {
                return new ActiveXObject(prefixes[i] + '.XMLHTTP');
            } catch (ex) {}
        }
        return null;
    }

    function getTarget(e) {
        var e = e || window.event;
        return e.target || e.srcElement;
    }

    function getClasses(element) {
        return element.className.trim().split(/\s+/);
    }

    function hasClass(element, c) {
        return getClasses(element).indexOf(c) != -1;
    }

    String.prototype.getAttr = function(attr) {
        var pattern = new RegExp(attr + "=(['\"])([^'\"]+)\\1");
        var m;
        if ((m = this.match(pattern)) != null) {
            return m[2];
        }
        return '';
    }

    String.prototype.trim = function() {
        return this.replace(/^\s+/,'').replace(/\s+$/,'');
    }

    Array.prototype.indexOf = function(item, start) {
        for (var i = (start || 0); i < this.length; i++) {
            if (this[i] == item) {
                return i;
            }
        }
        return -1;
    }

}

ajaxPageNamespace();
