(function () {
// const historySize = 10;
    const historySize = 0;
    const scrollHistorySize = 20;
    let loaderSet = false;

    function uriContainsParameter( href, name )
    {
        if (-1 !== href.indexOf('&' + name + '=')) {
            return true;
        }
        if (-1 !== href.indexOf('?' + name + '=')) {
            return true;
        }
        return false;
    }

    function getUriParameter(href, name)
    {
        let vars = {};
        const parts = href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (m,key,value) {
            vars[key] = value;});
        return vars[name];
    }

// https://plainjs.com/javascript/ajax/send-ajax-get-and-post-requests-47/
    function getAjax(url, funcSuccess)
    {
        var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
        xhr.open('GET', url);
        xhr.onreadystatechange = function () {
            if (xhr.readyState <= 3) {
                return;
            }
            if (200 == xhr.status) {
                funcSuccess(xhr.responseText, xhr.responseURL);
            } else {
                funcSuccess(xhr.responseText, xhr.responseURL);
            }
        };
        xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
        xhr.send();
        return xhr;
    }

    function postAjax(url, data, funcSuccess)
    {
        var params = typeof data == 'string' ? data : Object.keys(data).map(
            function (k) {
                return encodeURIComponent(k) + '=' + encodeURIComponent(data[k]) }
        ).join('&');

        var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
        xhr.open('POST', url);
        xhr.onreadystatechange = function () {
            if ( xhr.readyState <= 3 ) {
                return;
            }
            if ( 200 == xhr.status ) {
                funcSuccess(xhr.responseText, xhr.responseURL);
            } else {
                funcSuccess(xhr.responseText, xhr.responseURL);
            }
        };
        xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        xhr.send(params);
        return xhr;
    }

// example request
// postAjax('http://foo.bar/', 'p1=1&p2=Hello+World', function(data){ console.log(data); });
// postAjax('http://foo.bar/', { p1: 1, p2: 'Hello World' }, function(data){ console.log(data); });

    function getFormData( form )
    {
        if ( typeof form != 'object' ) {
            return '';
        }
        if ( 'FORM' != form.nodeName ) {
            return '';
        }

        let field, l, s = [];
        let ret = [];

        const len = form.elements.length;
        for ( let i = 0; i < len; i++ ) {
            field = form.elements[i];

            if ( ! field.name ) {
                continue;
            }
            if ( field.disabled ) {
                continue;
            }
            if ( 'reset' == field.type ) {
                continue;
            }
            if ( 'submit' == field.type ) {
                continue;
            }
            if ( 'button' == field.type ) {
                continue;
            }
            // if( 'file' == field.type ) continue;

            if ( 'select-multiple' == field.type ) {
                l = form.elements[i].options.length;
                for (var j = 0; j < l; j++) {
                    if (field.options[j].selected) {
                        ret[ ret.length ] = [ field.name, field.options[j].value ];
                    }
                }
            } else {
                if ( 'checkbox' == field.type ) {
                    if ( ! field.checked ) {
                        continue;
                    }
                }
                if ( 'radio' == field.type ) {
                    if ( ! field.checked ) {
                        continue;
                    }
                }
                ret[ ret.length ] = [ field.name,  field.value ];
            }
        }

        return ret;
    }

    function serializeData( data, thisParamPrefix )
    {
        if ( ! thisParamPrefix ) {
            thisParamPrefix = '';
        }

        let ret = [];
        for ( let i = 0; i < data.length; i++ ) {
            ret[ ret.length ] = encodeURIComponent(thisParamPrefix + data[i][0]) + "=" + encodeURIComponent(data[i][1]);
        }
        return ret.join('&').replace(/%20/g, '+');
    }

    function findLink( el )
    {
        if ( ('A' == el.tagName) && el.href ) {
            return el;
        }
        return el.parentElement ? findLink(el.parentElement) : null;
    }

    function setLoader( el )
    {
        if ( loaderSet ) {
            return;
        }
        el.classList.add('pw-loading');
        loaderSet = true;
    }

    function unsetLoader( el )
    {
        el.classList.remove('pw-loading');
        loaderSet = false;
    }

    function canAccessLocalStorage()
    {
        var test = 'plainware:testLocalStorage';
        try {
            localStorage.setItem(test, test);
            localStorage.removeItem(test);
            return true;
        } catch (e) {
            return false;
        }
    }

    function loadFromHistory( url )
    {
        if (!canAccessLocalStorage()) {
            return null;
        }
        if (!historySize) {
            return null;
        }

        var historyList = JSON.parse(localStorage.getItem('plainware-history')) || [];
        for ( var i = 0; i < historyList.length; i++ ) {
            if ( url == historyList[i].url ) {
                return historyList[i];
                break;
            }
        }

        return null;
    }

    function saveScrollPosition(href, scrollY)
    {
        if (!canAccessLocalStorage()) {
            return;
        }

        var newHistoryItem = {href:href, scroll: scrollY};

        var listHistory = JSON.parse(localStorage.getItem('plainware-scroll-history')) || [];
        for (var i = 0; i < listHistory.length; i++) {
            if (listHistory[i].href === newHistoryItem.href) {
                listHistory.splice(i, 1);
                break;
            }
        }

        listHistory.push(newHistoryItem);
        while (listHistory.length > scrollHistorySize) {
            listHistory.shift();
        }

        while (listHistory.length > 0) {
            try {
                localStorage.setItem('plainware-scroll-history', JSON.stringify(listHistory));
                break;
            } catch (e) {
                listHistory.shift(); // shrink the cache and retry
            }
        }

        // console.log( 'Save scroll: ' + newHistoryItem.href + ' => ' + newHistoryItem.scroll );
    }

    function findScrollPosition(href)
    {
        if (!canAccessLocalStorage()) {
            return -1;
        }
        if (!scrollHistorySize) {
            return -1;
        }

        var listHistory = JSON.parse(localStorage.getItem('plainware-scroll-history')) || [];
        for (var i = 0; i < listHistory.length; i++) {
            if (document.location.href == listHistory[i].href) {
                // console.log( 'Got scroll: ' + listHistory[i].href + ' => ' + listHistory[i].scroll );
                return listHistory[i].scroll;
                break;
            }
        }

        return -1;
    }

    function saveToHistory(url, content)
    {
        if (!canAccessLocalStorage()) {
            return;
        }
        if (!historySize) {
            return;
        }

        var newHistoryItem = {url:url, content: content, scroll: window.scrollY};
        // console.log( 'saveToHistory:' +  newHistoryItem.url );

        var historyList = JSON.parse(localStorage.getItem('plainware-history')) || [];
        for (var i = 0; i < historyList.length; i++) {
            if (historyList[i].url === url) {
                historyList.splice(i, 1);
                break;
            }
        }

        historyList.push(newHistoryItem);
        while (historyList.length > historySize) {
            historyList.shift();
        }

        while (historyList.length > 0) {
            try {
                localStorage.setItem('plainware-history', JSON.stringify(historyList));
                break;
            } catch (e) {
                historyList.shift(); // shrink the cache and retry
            }
        }
        // console.log( 'history size ' + historyList.length );
    }

    function ajaxContainer( $container )
    {
    // console.log( 'AJAXCONTAINER' );

        // var fetchCount = 0;
        var pathParamName = $container.getAttribute('data-pw-path-param-name');
        if (null === pathParamName) {
            pathParamName = 'p';
        }

        var layoutParamName = $container.getAttribute('data-pw-layout-param-name');
        if (null === layoutParamName) {
            layoutParamName = 'layout-';
        }

        window.onpopstate = function (e) {
            var url = document.location.href;

            var storedItem = loadFromHistory(url);
            if (null !== storedItem) {
    // console.log( 'has content for ' + url );
                setContent($container, storedItem.content);
            } else {
    // console.log( 'no content for ' + url );
                loadUrl(url, $container, $container, '');
            }
        };

        function convertUrl(href, layoutParamValue)
        {
        // tell to load partial only
            var glue = (-1 !== href.indexOf('?')) ? '&' : '?';
            href = href + glue + layoutParamName + '=' + layoutParamValue;

        // wordpress admin? change url to admin-ajax.php
            if (typeof ajaxurl !== 'undefined') {
                if (-1 !== href.indexOf('admin.php?page=')) {
                    href = href.replace('admin.php?page=', 'admin-ajax.php?action=');
                }
            }

            return href;
        }

        function deconvertUrl( href )
        {
        // has layout param?
            var pos = href.indexOf('?' + layoutParamName + '=');
            if (-1 == pos) {
                var pos = href.indexOf('&' + layoutParamName + '=');
            }

            if (-1 !== pos) {
            // anything after our layout param?
                var pos2 = href.indexOf('&', pos + 1);
                if ( -1 !== pos2 ) {
                    // href = href.substring( 0, pos ) + href.substring( pos2 );
                    var href1 = href.substring(0, pos);
                    var href2 = href.substring(pos2 + 1);
                    var glue = ( -1 !== href1.indexOf('?') ) ? '&' : '?';
                    href = href1 + glue + href2;
                } else {
                    href = href.substring(0, pos);
                }
            }

        // wordpress admin? change url from admin-ajax.php back to admin.php
            if (typeof ajaxurl !== 'undefined') {
                if (-1 !== href.indexOf('admin-ajax.php?action=')) {
                    href = href.replace('admin-ajax.php?action=', 'admin.php?page=');
                }
            }

            return href;
        }

        function clickLink(e)
        {
            var a = findLink(e.target);
            if (null === a) {
                return;
            }

        // target = _blank?
            var aTarget = a.getAttribute('target');
            if ('_blank' == aTarget) {
                return;
            }

            var href = a.href;

        // contains our path
            if (!uriContainsParameter(href, pathParamName)) {
                return;
            }

        // already contains layout param?
            if (uriContainsParameter(href, layoutParamName)) {
                return;
            }

            e.preventDefault();

            var isAsync = a.hasAttribute('data-pw-async');

        // has data-pw-target parameter that may redefine the target?
            var keepScroll = a.hasAttribute('data-pw-keep-scroll');
            if (keepScroll) {
                saveScrollPosition(href, window.scrollY);
            }

            if (isAsync) {
                loadAsyncUrl(href);
                toggleAsyncLoader(a);
            } else {
            // find target element
                var $thisContainer;
                var targetPath = getUriParameter(href, pathParamName);

                if (!$thisContainer) {
                    $thisContainer = $container;
                }

                loadUrl(href, $thisContainer, a, 'ajax');
            }
        }

        function loadPartialUrl( uri, el, partial )
        {
            var realUri = convertUrl(uri, partial);
            console.log('GET:' + realUri);

            var holder = el;
            while (holder) {
                if (holder == $container) {
                    break;
                }
                // console.log( holder );
                if ('none' === window.getComputedStyle(holder).display) {
                    holder.style.display = 'block';
                }
                holder = holder.parentNode;
            }

        // temporarily hide header in holder
            var holderHeader = el.parentNode.querySelector('header');
            if ( holderHeader ) {
                holderHeader.style.display = 'none';
            }
            el.innerHTML = '';

            setLoader(el);

            getAjax(realUri, function (data, finalUri) {
                if ( realUri != finalUri ) {
                    uri = deconvertUrl(finalUri);
                    // console.log( 'deconvert' + "\n" + finalUri + "\n" + uri );
                }

                unsetLoader(el);
                setContent(el, data);
                if ( holderHeader ) {
                    holderHeader.style.display = 'block';
                }
            });

            return false;
        }

        function loadUrl(uri, targetEl, srcEl, layoutParamValue)
        {
            saveScrollPosition(document.location.href, window.scrollY);

            // var realUri = convertUrl( uri, '' );
            var realUri = convertUrl(uri, layoutParamValue);
    // fetchCount++;
    // console.log( fetchCount + ':' + 'GET:' + realUri );
            console.log('ASKGET:' + uri);
            console.log('REALGET:' + realUri);

            // var markEl = $container;
            var markEl = srcEl;
            setLoader(markEl);

            getAjax(realUri, function (data, finalUri) {
            // after redirect?
    // alert( realUri + ' VS ' + finalUri );
    // console.log( 'after redirect: ' + "\n" + realUri + "\n" + finalUri );
                if ( realUri != finalUri ) {
                    uri = deconvertUrl(finalUri);
                    console.log('deconvert' + "\n" + finalUri + "\n" + uri);
                }

                unsetLoader(markEl);
                setContent(targetEl, data);

                if ( window.history.pushState ) {
                    window.history.pushState({}, null, uri);
                    saveToHistory(uri, data);
                }

                var scrollY = findScrollPosition(uri);
                if (-1 != scrollY) {
                    window.scroll(0, scrollY);
                } else {
                    let elHeight = targetEl.offsetTop + targetEl.offsetHeight;
                    // alert(elHeight + ' vs ' + window.innerHeight);
                    // if (elHeight > window.innerHeight) {
                        // targetEl.scrollIntoView();
                    // }
                    targetEl.scrollIntoView();
                }
            });

            return false;
        }

        function loadAsyncUrl(uri)
        {
            var realUri = convertUrl(uri, 'ajax');
            console.log('GETASYNC:' + realUri);
            getAjax(realUri, function (data, finalUri) {
                console.log('GOTASYNC:' + finalUri);
            });

            return false;
        }

        function toggleAsyncLoader( el )
        {
        // if it is inside data-pw-async-toggler then toggle display
            var par = el.parentNode;
            while ( par ) {
                if ( par.hasAttribute('data-pw-async-toggler') ) {
                    break;
                }
                par = par.parentNode;
            }

            if ( par ) {
                par.querySelectorAll('a').forEach(function (e) {
                    e.style.display = ( 'none' === window.getComputedStyle(e).display ) ? 'block' : 'none';
                });
            }
        }

        function toggleAsyncLoader_( el )
        {
        // direct children only
            for ( const e of el.children ) {
                if ( 'none' === window.getComputedStyle(e).display ) {
                    e.style.display = 'block';
                } else {
                    e.style.display = 'none';
                }
            }
        }

        function submitForm( e )
        {
            var href = e.target.getAttribute('action');
            if (!href) {
                href = document.location.href;
            }
            if (!href) {
                console.log('no action uri for form');
                return;
            }

            var noAjax = e.target.getAttribute('data-pw-noajax');
            if (noAjax) {
                return;
            }

            var withFileUpload = ( 'multipart/form-data' == e.target.getAttribute('enctype') ) ? true : false;
            if ( withFileUpload ) {
                return;
            }

        // contains our path
            if (!uriContainsParameter(href, pathParamName)) {
                return;
            }

            e.preventDefault();

            // var markEl = $container;
            var markEl = e.submitter ? e.submitter : e.target;
            setLoader(markEl);

            const form = e.target;
            let data = getFormData(form);

            var realUri = convertUrl(href, 'ajax');
            console.log('POST:' + realUri);

            if ( e.submitter && e.submitter.name ) {
                data[ data.length ] = [ e.submitter.name, e.submitter.value ];
            }

            let dataString = serializeData(data);
    // console.log( data );

            postAjax(realUri, dataString, function (data, finalUri) {
                if (realUri != finalUri) {
                    uri = deconvertUrl(finalUri);
                    // console.log( 'deconvert' + "\n" + finalUri + "\n" + uri );
                } else {
                    uri = deconvertUrl(realUri);
                }

                unsetLoader(markEl);
                setContent($container, data);

                if ( window.history.pushState ) {
    // console.log( 'push ' + uri );
                    window.history.pushState({}, null, uri);
                    // saveToHistory( uri, data );
                }

                var scrollY = findScrollPosition(uri);
                if ( scrollY ) {
                    window.scroll(0, scrollY);
                } else {
                    $container.scrollIntoView();
                }
            });

            return false;
        };

        function setContent( el, content )
        {
            el.innerHTML = content;

        // inline script
            el.querySelectorAll('script').forEach(function (e) {
                // alert( 'evaling' );
                // eval( e.innerHTML );
                eval.call(window, e.innerHTML);
            });

        // emit event
            var ev = new CustomEvent('pwContentLoaded', {el: el, content: content});
            document.dispatchEvent(ev);
        }

        $container.addEventListener('click', clickLink, false);
        $container.addEventListener('submit', submitForm, false);
    }

    document.addEventListener('DOMContentLoaded', function () {
        var containerList = document.getElementsByClassName('pw-ajax-container');
        for (var ii = 0; ii < containerList.length; ii++) {
            ajaxContainer(containerList[ii]);
        }
    });
})();