require('whatwg-fetch');
require('element-closest');
require('classlist.js');
require('url-search-params-polyfill');
let Awesomplete = require('awesomplete');
import * as killy from './embed';

if (!('remove' in Element.prototype)) { // Firefox <23
    Element.prototype.remove = function () {
        if (this.parentNode) {
            this.parentNode.removeChild(this);
        }
    };
}

NodeList.prototype.forEach = Array.prototype.forEach;
HTMLCollection.prototype.forEach = Array.prototype.forEach;

NodeList.prototype.filter = Array.prototype.filter;
HTMLCollection.prototype.filter = Array.prototype.filter;

Element.prototype.selectText = function () {
    let d = document;
    if (d.body.createTextRange) {
        let range = d.body.createTextRange();
        range.moveToElementText(this);
        range.select();
    } else if (window.getSelection) {
        let selection = window.getSelection();
        let rangeSel = d.createRange();
        rangeSel.selectNodeContents(this);
        selection.removeAllRanges();
        selection.addRange(rangeSel);
    }
};

function autosize(el) {
    let offset = (!window.opera)
        ? (el.offsetHeight - el.clientHeight)
        : (el.offsetHeight + parseInt(window.getComputedStyle(el, null).getPropertyValue('border-top-width')));

    let resize = function (el) {
        el.style.height = 'auto';
        el.style.height = (el.scrollHeight + offset) + 'px';
    };

    if (el.addEventListener) {
        el.addEventListener('input', () => resize(el));
    } else if (el.attachEvent) {
        el.attachEvent('onkeyup', () => resize(el));
    }
}

function evilIcon(name) {
    return `<div class="icon icon--${name}"><svg class="icon__cnt"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#${name}-icon"></use></svg></div>`;
}

/* eslint-disable only-ascii/only-ascii */
const translations = {
    'en': {
        'message.inReplyTo': 'in reply to',
        'message.reply': 'Reply',
        'message.likeThisMessage?': 'Recommend this message?',
        'postForm.pleaseInputMessageText': 'Please input message text',
        'postForm.upload': 'Upload',
        'postForm.newMessage': 'New message...',
        'postForm.imageLink': 'Link to image',
        'postForm.imageFormats': 'JPG/PNG, up to 10 MB',
        'postForm.or': 'or',
        'postForm.tags': 'Tags (space separated)',
        'postForm.submit': 'Send',
        'comment.writeComment': 'Write a comment...',
        'shareDialog.linkToMessage': 'Link to message',
        'shareDialog.messageNumber': 'Message number',
        'shareDialog.share': 'Share',
        'loginDialog.pleaseIntroduceYourself': 'Please introduce yourself',
        'loginDialog.registeredAlready': 'Registered already?',
        'loginDialog.username': 'Username',
        'loginDialog.password': 'Password',
        'loginDialog.facebook': 'Login with Facebook',
        'loginDialog.vk': 'Login with VK',
        'loginDialog.email': 'Registration',
        'error.error': 'Error'
    },
    'ru': {
        'message.inReplyTo': 'в ответ на',
        'message.reply': 'Ответить',
        'message.likeThisMessage?': 'Рекомендовать это сообщение?',
        'postForm.pleaseInputMessageText': 'Пожалуйста, введите текст сообщения',
        'postForm.upload': 'загрузить',
        'postForm.newMessage': 'Новое сообщение...',
        'postForm.imageLink': 'Ссылка на изображение',
        'postForm.imageFormats': 'JPG/PNG, до 10Мб',
        'postForm.or': 'или',
        'postForm.tags': 'Теги (через пробел)',
        'postForm.submit': 'Отправить',
        'comment.writeComment': 'Написать комментарий...',
        'shareDialog.linkToMessage': 'Ссылка на сообщение',
        'shareDialog.messageNumber': 'Номер сообщения',
        'shareDialog.share': 'Поделиться',
        'loginDialog.pleaseIntroduceYourself': 'Пожалуйста, представьтесь',
        'loginDialog.registeredAlready': 'Уже зарегистрированы?',
        'loginDialog.username': 'Имя пользователя',
        'loginDialog.password': 'Пароль',
        'loginDialog.facebook': 'Войти через Facebook',
        'loginDialog.vk': 'Войти через ВКонтакте',
        'loginDialog.email': 'Регистрация',
        'error.error': 'Ошибка'
    }
};
/* eslint-enable only-ascii/only-ascii */

function getLang() {
    return (window.navigator.languages && window.navigator.languages[0])
        || window.navigator.userLanguage
        || window.navigator.language;
}
function i18n(key, lang = undefined) {
    const fallbackLang = 'ru';
    lang = lang || getLang().split('-')[0];
    return (translations[lang] && translations[lang][key])
        || translations[fallbackLang][key]
        || key;
}

var ws,
    pageTitle;

function initWS() {
    let url = (window.location.protocol === 'https:' ? 'wss' : 'ws') + ':'
        + '//api.juick.com/ws/';
    let hash = document.getElementById('body').getAttribute('data-hash');
    if (hash) {
        url += '?hash=' + hash;
    } else {
        let content = document.getElementById('content');
        if (content) {
            let pageMID = content.getAttribute('data-mid');
            if (pageMID) {
                url += pageMID;
            }
        }
    }

    ws = new WebSocket(url);
    ws.onopen = function () {
        console.log('online');
        if (!document.querySelector('#wsthread')) {
            var d = document.createElement('div');
            d.id = 'wsthread';
            d.addEventListener('click', nextReply);
            document.querySelector('body').appendChild(d);
            pageTitle = document.title;
        }
    };
    ws.onclose = function () {
        console.log('offline');
        ws = false;
        setTimeout(function () {
            initWS();
        }, 2000);
    };
    ws.onmessage = function (msg) {
        if (msg.data == ' ') {
            ws.send(' ');
        } else {
            try {
                var jsonMsg = JSON.parse(msg.data);
                console.log('data: ' + msg.data);
                if (jsonMsg.service) {
                    return;
                }
                wsIncomingReply(jsonMsg);
            } catch (err) {
                console.log(err);
            }
        }
    };
    setInterval(wsSendKeepAlive, 90000);
}

function wsSendKeepAlive() {
    if (ws) {
        ws.send(' ');
    }
}

function wsShutdown() {
    if (ws) {
        ws.onclose = function () { };
        ws.close();
    }
}

function wsIncomingReply(msg) {
    let content = document.getElementById('content');
    if (!content) { return; }
    let pageMID = content.getAttribute('data-mid');
    if (!pageMID || pageMID != msg.mid) { return; }
    let msgNum = '/' + msg.rid;
    if (msg.replyto > 0) {
        msgNum += ` ${i18n('message.inReplyTo')} <a href="#${msg.replyto}">/${msg.replyto}</a>`;
    }
    let photoDiv = (msg.attach == null) ? '' : `
            <div class="msg-media"><a href="//i.juick.com/p/${msg.mid}-${msg.rid}.${msg.attach}">
                <img src="//i.juick.com/photos-512/${msg.mid}-${msg.rid}.${msg.attach}"/></a>
            </div>`;
    let msgContHtml = `
        <div class="msg-cont">
            <div class="msg-header">
                <a href="/${msg.user.uname}/">${msg.user.uname}</a>:
                <div class="msg-avatar">
                    <a href="/${msg.user.uname}/"><img src="//i.juick.com/a/${msg.user.uid}.png" alt="${msg.user.uname}"/></a>
                </div>
                <div class="msg-ts">
                    <a href="/m/${msg.mid}#${msg.rid}" title="${msg.timestamp}GMT">${msg.timestamp}</a>
                </div>
            </div>
            <div class="msg-txt">${killy.format(msg.body, msg.mid, false)}</div>${photoDiv}
            <div class="msg-links">${msgNum} &middot; <a class="msg-reply-link" href="#">${i18n('message.reply')}</a></div>
            <div class="msg-comment-target msg-comment-hidden"></div>
        </div>`;

    let li = document.createElement('li');
    li.setAttribute('class', 'msg reply-new');
    li.setAttribute('id', msg.rid);
    li.innerHTML = msgContHtml;
    li.addEventListener('click', newReply);
    li.addEventListener('mouseover', newReply);
    li.querySelector('a.msg-reply-link').addEventListener('click', function (e) {
        showCommentForm(msg.mid, msg.rid);
        e.preventDefault();
    });

    killy.embedLinksToX(li.querySelector('.msg-cont'), '.msg-links', '.msg-txt a');

    document.getElementById('replies').appendChild(li);

    updateRepliesCounter();
}

function newReply(e) {
    var li = e.target;
    li.classList.remove('reply-new');
    li.removeEventListener('click', e);
    li.removeEventListener('mouseover', e);
    updateRepliesCounter();
}

function nextReply() {
    var li = document.querySelector('#replies>li.reply-new');
    if (li) {
        li.classList.remove('reply-new');
        li.removeEventListener('click', this);
        li.children[0].scrollIntoView();
        updateRepliesCounter();
    }
}

function updateRepliesCounter() {
    var replies = document.querySelectorAll('#replies>li.reply-new').length;
    var wsthread = document.getElementById('wsthread');
    if (replies) {
        wsthread.textContent = replies;
        wsthread.style.display = 'block';
        document.title = '[' + replies + '] ' + pageTitle;
    } else {
        wsthread.style.display = 'none';
        document.title = pageTitle;
    }
}

/******************************************************************************/
/******************************************************************************/
/******************************************************************************/

function postformListener(formEl, ev) {
    if (ev.ctrlKey && (ev.keyCode == 10 || ev.keyCode == 13)) {
        let form = formEl.closest('form');
        if (!form.onsubmit || form.onsubmit()) {
            form.submit();
        }
    }
}
function closeDialogListener(ev) {
    ev = ev || window.event;
    if (ev.keyCode == 27) {
        closeDialog();
    }
}

function newMessage(evt) {
    document.querySelectorAll('#newmessage .dialogtxt').forEach(t => {
        t.remove();
    });
    if (document.querySelector('#newmessage textarea').value.length == 0
        && document.querySelector('#newmessage .img').value.length == 0
        && !document.querySelector('#newmessage input[type="file"]')) {
        document.querySelector('#newmessage').insertAdjacentHTML('afterbegin', `<p class="dialogtxt">${i18n('postForm.pleaseInputMessageText')}</p>`);
        evt.preventDefault();
    }
}

function showCommentForm(mid, rid) {
    let reply = document.getElementById(rid);
    let formTarget = reply.querySelector('div.msg-cont .msg-comment-target');
    if (formTarget) {
        let formHtml = `
            <form action="/comment" method="POST" enctype="multipart/form-data">
                <input type="hidden" name="mid" value="${mid}">
                <input type="hidden" name="rid" value="${rid}">
                <div class="msg-comment">
                    <div class="ta-wrapper">
                        <textarea name="body" rows="1" class="reply" placeholder="${i18n('comment.writeComment')}"></textarea>
                        <div class="attach-photo">${evilIcon('ei-camera')}</div>
                    </div>
                    <input type="submit" value="OK">
                </div>
            </form>`;
        formTarget.insertAdjacentHTML('afterend', formHtml);
        formTarget.remove();

        let form = reply.querySelector('form');
        let submitButton = form.querySelector('input[type="submit"]');

        let attachButton = form.querySelector('.msg-comment .attach-photo');
        attachButton.addEventListener('click', e => attachCommentPhoto(e.target));

        let textarea = form.querySelector('.msg-comment textarea');
        textarea.addEventListener('keypress', e => postformListener(e.target, e));
        autosize(textarea);

        let validateMessage = () => {
            let len = textarea.value.length;
            if (len > 4096) { return 'Message is too long'; }
            return '';
        };
        form.addEventListener('submit', e => {
            let validationResult = validateMessage();
            if (validationResult) {
                e.preventDefault();
                alert(validationResult);
                return false;
            }
            submitButton.disabled = true;
        });
    }
    reply.querySelector('.msg-comment textarea').focus();
}

function attachInput() {
    let inp = document.createElement('input');
    inp.setAttribute('type', 'file');
    inp.setAttribute('name', 'attach');
    inp.setAttribute('accept', 'image/jpeg,image/png');
    inp.style.visibility = 'hidden';
    return inp;
}

function attachCommentPhoto(div) {
    let input = div.querySelector('input');
    if (input) {
        input.remove();
        div.classList.remove('attach-photo-active');
    } else {
        let newInput = attachInput();
        newInput.addEventListener('change', function () {
            div.classList.add('attach-photo-active');
        });
        newInput.click();
        div.appendChild(newInput);
    }
}

function attachMessagePhoto(div) {
    var f = div.closest('form'),
        finput = f.querySelector('input[type="file"]');
    if (!finput) {
        var inp = attachInput();
        inp.style.float = 'left';
        inp.style.width = 0;
        inp.style.height = 0;
        inp.addEventListener('change', function () {
            div.textContent = i18n('postForm.upload') + ' (✓)';
        });
        f.appendChild(inp);
        inp.click();
    } else {
        finput.remove();
        div.textContent = i18n('postForm.upload');
    }
}

function showMessageLinksDialog(mid, rid) {
    let hlink = window.location.protocol + '//juick.com/' + mid;
    let mlink = '#' + mid;
    if (rid > 0) {
        hlink += '#' + rid;
        mlink += '/' + rid;
    }
    let hlinkenc = encodeURIComponent(hlink);
    let html = `
        <div class="dialogshare">
            ${i18n('shareDialog.linkToMessage')}: <div onclick="this.selectText()" class="dialogl">${hlink}</div>
            ${i18n('shareDialog.messageNumber')}: <div onclick="this.selectText()" class="dialogl">${mlink}</div>
            ${i18n('shareDialog.share')}:
            <ul>
                <li><a href="https://www.facebook.com/sharer/sharer.php?u=${hlinkenc}" onclick="return openSocialWindow(this)">${evilIcon('ei-sc-facebook')}</a></li>
                <li><a href="https://twitter.com/intent/tweet?url=${hlinkenc}" onclick="return openSocialWindow(this)">${evilIcon('ei-sc-twitter')}</a></li>
                <li><a href="https://vk.com/share.php?url=${hlinkenc}" onclick="return openSocialWindow(this)">${evilIcon('ei-sc-vk')}</a></li>
            </ul>
        </div>`;

    openDialog(html);
}

function showPhotoDialog(fname) {
    let width = window.innerWidth;
    let height = window.innerHeight;
    let minDimension = (width < height) ? width : height;
    if (minDimension < 640) {
        return true; // no dialog, open the link
    } else if (minDimension < 1280) {
        openDialog(`<a href="//i.juick.com/p/${fname}"><img src="//i.juick.com/photos-1024/${fname}"/></a>`, true);
        return false;
    } else {
        openDialog(`<a href="//i.juick.com/p/${fname}"><img src="//i.juick.com/p/${fname}"/></a>`, true);
        return false;
    }
}

function openPostDialog() {
    let newmessageTemplate = `
        <form id="newmessage" action="/post" method="post" enctype="multipart/form-data">
            <textarea name="body" placeholder="${i18n('postForm.newMessage')}"></textarea>
            <div>
                <input class="img" name="img" placeholder="${i18n('postForm.imageLink')} (${i18n('postForm.imageFormats')})"/>
                 ${i18n('postForm.or')} <a href="#">${i18n('postForm.upload')}</a><br/>
                <input id="tags_input" class="tags" name="tags" placeholder="${i18n('postForm.tags')}"/><br/>
                <input type="submit" class="subm" value="${i18n('postForm.submit')}"/>
            </div>
        </form>
    `;
    return openDialog(newmessageTemplate);
}

function openDialog(html, image) {
    var dialogHtml = `
        <div id="dialogt">
            <div id="dialogb"></div>
            <div id="dialogw">
                <div id="dialog_header">
                    <div id="dialogc">${evilIcon('ei-close')}</div>
                </div>
                ${html}
            </div>
        </div>`;
    let body = document.querySelector('body');
    body.classList.add('dialog-opened');
    body.insertAdjacentHTML('afterbegin', dialogHtml);
    if (image) {
        let header = document.querySelector('#dialog_header');
        header.classList.add('header_image');
    }
    document.addEventListener('keydown', closeDialogListener);
    document.querySelector('#dialogb').addEventListener('click', closeDialog);
    document.querySelector('#dialogc').addEventListener('click', closeDialog);
}

function closeDialog() {
    let draft = document.querySelector('#newmessage textarea');
    if (draft) {
        window.draft = draft.value;
    }
    document.querySelector('body').classList.remove('dialog-opened');
    document.querySelector('#dialogb').remove();
    document.querySelector('#dialogt').remove();
}

function openSocialWindow(a) {
    var w = window.open(a.href, 'juickshare', 'width=640,height=400');
    if (window.focus) { w.focus(); }
    return false;
}

function checkUsername() {
    var uname = document.querySelector('#username').textContent,
        style = document.querySelector('#username').style;
    fetch('//api.juick.com/users?uname=' + uname)
        .then(function () {
            style.background = '#FFCCCC';
        })
        .catch(function () {
            style.background = '#CCFFCC';
        });
}

/******************************************************************************/

function openDialogLogin() {
    let html = `
        <div class="dialoglogin">
            <p>${i18n('loginDialog.pleaseIntroduceYourself')}:</p>
            <a href="mailto:juick@juick.com?subject=LOGIN" id="signemail">${evilIcon('ei-envelope')}${i18n('loginDialog.email')}</a>
            <a href="/_fblogin" id="signfb">${evilIcon('ei-sc-facebook')}${i18n('loginDialog.facebook')}</a>
            <a href="/_vklogin" id="signvk">${evilIcon('ei-sc-vk')}${i18n('loginDialog.vk')}</a>
            <p>${i18n('loginDialog.registeredAlready')}</p>
            <form action="/login" method="POST">
                <input class="signinput" type="text" name="username" placeholder="${i18n('loginDialog.username')}"/><br/>
                <input class="signinput" type="password" name="password" placeholder="${i18n('loginDialog.password')}"/><br/>
                <input class="signsubmit" type="submit" value="OK"/>
            </form>
        </div>`;
    openDialog(html);
    return false;
}

/******************************************************************************/

function resultMessage(str) {
    var result = document.createElement('p');
    result.textContent = str;
    return result;
}

function likeMessage(e, mid) {
    if (confirm(i18n('message.likeThisMessage?'))) {
        fetch('//api.juick.com/like?mid=' + mid
            + '&hash=' + document.getElementById('body').getAttribute('data-hash'), {
                method: 'POST',
                mode: 'cors',
                credentials: 'omit'
            })
            .then(function (response) {
                if (response.ok) {
                    e.closest('article').appendChild(resultMessage('OK!'));
                }
            })
            .catch(function () {
                e.closest('article').appendChild(resultMessage(i18n('error.error')));
            });
    }
    return false;
}

/******************************************************************************/

function setPopular(e, mid, popular) {
    fetch('//api.juick.com/messages/set_popular?mid=' + mid
        + '&popular=' + popular
        + '&hash=' + document.getElementById('body').getAttribute('data-hash'), {
            credentials: 'same-origin'
        })
        .then(function () {
            e.closest('article').append(resultMessage('OK!'));
        });
    return false;
}

function setPrivacy(e, mid) {
    fetch('//api.juick.com/messages/set_privacy?mid=' + mid
        + '&hash=' + document.getElementById('body').getAttribute('data-hash'), {
            credentials: 'same-origin'
        })
        .then(function () {
            e.closest('article').append(resultMessage('OK!'));
        });
    return false;
}

function getTags() {
    fetch('//api.juick.com/tags?hash=' + document.getElementById('body').getAttribute('data-hash'), {
        credentials: 'same-origin'
    })
        .then(response => {
            return response.json();
        })
        .then(json => {
            let tags = json.map(t => t.tag);
            let input = document.getElementById('tags_input');
            new Awesomplete(input, { list: tags });
        });
    return false;
}

function addTag(tag) {
    document.forms['postmsg'].body.value = '*' + tag + ' ' + document.forms['postmsg'].body.value;
    return false;
}

/******************************************************************************/

function ready(fn) {
    if (document.readyState != 'loading') {
        fn();
    } else {
        document.addEventListener('DOMContentLoaded', fn);
    }
}

ready(function () {
    document.querySelectorAll('textarea').forEach((ta) => {
        autosize(ta);
    });

    var insertPMButtons = function (e) {
        e.target.classList.add('narrowpm');
        e.target.parentNode.insertAdjacentHTML('afterend', '<input type="submit" value="OK"/>');
        e.target.removeEventListener('click', insertPMButtons);
        e.preventDefault();
    };
    document.querySelectorAll('textarea.replypm').forEach(function (e) {
        e.addEventListener('click', insertPMButtons);
        e.addEventListener('keypress', function (e) {
            postformListener(e.target, e);
        });
    });
    document.querySelectorAll('#postmsg textarea').forEach(function (e) {
        e.addEventListener('keypress', function (e) {
            postformListener(e.target, e);
        });
    });

    var content = document.getElementById('content');
    if (content) {
        var pageMID = content.getAttribute('data-mid');
        if (pageMID > 0) {
            document.querySelectorAll('li.msg').forEach(li => {
                let showReplyFormBtn = li.querySelector('.a-thread-comment');
                if (showReplyFormBtn) {
                    showReplyFormBtn.addEventListener('click', function (e) {
                        showCommentForm(pageMID, li.id);
                        e.preventDefault();
                    });
                }
            });
            let opMessage = document.querySelector('.msgthread');
            if (opMessage) {
                let replyTextarea = opMessage.querySelector('textarea.reply');
                if (replyTextarea) {
                    replyTextarea.addEventListener('focus', e => showCommentForm(pageMID, 0));
                    replyTextarea.addEventListener('keypress', e => postformListener(e.target, e));
                    if (!window.location.hash) {
                        replyTextarea.focus();
                    }
                }
            }
        }
    }

    var postmsg = document.getElementById('postmsg');
    if (postmsg) {
        document.querySelectorAll('a').filter(t => t.href.indexOf('?') >= 0).forEach(t => {
            t.addEventListener('click', e => {
                let params = new URLSearchParams(t.href.slice(t.href.indexOf('?') + 1));
                if (params.has('tag')) {
                    addTag(params.get('tag'));
                    e.preventDefault();
                }
            });
        });
    }

    document.querySelectorAll('.msg-menu').forEach(function (el) {
        el.addEventListener('click', function (e) {
            var reply = e.target.closest('li');
            var rid = reply ? parseInt(reply.id) : 0;
            var message = e.target.closest('section');
            var mid = message.getAttribute('data-mid') || e.target.closest('article').getAttribute('data-mid');
            showMessageLinksDialog(mid, rid);
            e.preventDefault();
        });
    });
    document.querySelectorAll('.l .a-privacy').forEach(function (e) {
        e.addEventListener('click', function (e) {
            setPrivacy(
                e.target,
                e.target.closest('article').getAttribute('data-mid'));
            e.preventDefault();
        });
    });
    document.querySelectorAll('.ir a[data-fname], .msg-media a[data-fname]').forEach(function (el) {
        el.addEventListener('click', function (e) {
            let fname = e.target.closest('[data-fname]').getAttribute('data-fname');
            if (!showPhotoDialog(fname)) {
                e.preventDefault();
            }
        });
    });
    document.querySelectorAll('.social a').forEach(function (e) {
        e.addEventListener('click', function (e) {
            openSocialWindow(e.target);
            e.preventDefault();
        });
    });
    var username = document.getElementById('username');
    if (username) {
        username.addEventListener('blur', function () {
            checkUsername();
        });
    }

    document.querySelectorAll('.l .a-like').forEach(function (e) {
        e.addEventListener('click', function (e) {
            likeMessage(
                e.target,
                e.target.closest('article').getAttribute('data-mid'));
            e.preventDefault();
        });
    });
    document.querySelectorAll('.a-login').forEach(function (el) {
        el.addEventListener('click', function (e) {
            openDialogLogin();
            e.preventDefault();
        });
    });
    var unfoldall = document.getElementById('unfoldall');
    if (unfoldall) {
        unfoldall.addEventListener('click', function (e) {
            document.querySelectorAll('#replies>li').forEach(function (e) {
                e.style.display = 'block';
            });
            document.querySelectorAll('#replies .msg-comments').forEach(function (e) {
                e.style.display = 'none';
            });
            e.preventDefault();
        });
    }
    document.querySelectorAll('article').forEach(function (article) {
        if (Array.prototype.some.call(
            article.querySelectorAll('.msg-tags a'),
            function (a) {
                return a.textContent === 'NSFW';
            }
        )) {
            article.classList.add('nsfw');
        }
    });
    initWS();

    window.addEventListener('pagehide', wsShutdown);

    killy.embedAll();
    var elSelector = 'header',
        elClassHidden = 'header--hidden',
        elClassBackground = 'header--background',
        throttleTimeout = 500,
        element = document.querySelector(elSelector);

    if (element) {

        var dHeight = 0,
            wHeight = 0,
            wScrollCurrent = 0,
            wScrollBefore = 0,
            wScrollDiff = 0,

            throttle = function (delay, fn) {
                var last, deferTimer;
                return function () {
                    var context = this, args = arguments, now = +new Date;
                    if (last && now < last + delay) {
                        clearTimeout(deferTimer);
                        deferTimer = setTimeout(
                            function () {
                                last = now;
                                fn.apply(context, args);
                            },
                            delay);
                    } else {
                        last = now;
                        fn.apply(context, args);
                    }
                };
            };

        window.addEventListener('scroll', throttle(throttleTimeout, function () {
            dHeight = document.body.offsetHeight;
            wHeight = window.innerHeight;
            wScrollCurrent = window.pageYOffset;
            wScrollDiff = wScrollBefore - wScrollCurrent;

            if (wScrollCurrent <= 0) {
                // scrolled to the very top; element sticks to the top
                element.classList.remove(elClassHidden);
                element.classList.remove(elClassBackground);
            } else if (wScrollDiff > 0 && element.classList.contains(elClassHidden)) {
                // scrolled up; element slides in
                element.classList.remove(elClassHidden);
                element.classList.add(elClassBackground);
            } else if (wScrollDiff < 0) {
                // scrolled down
                if (wScrollCurrent + wHeight >= dHeight && element.classList.contains(elClassHidden)) {
                    // scrolled to the very bottom; element slides in
                    element.classList.remove(elClassHidden);
                    element.classList.add(elClassBackground);
                } else {
                    // scrolled down; element slides out
                    element.classList.add(elClassHidden);
                }
            }

            wScrollBefore = wScrollCurrent;
        }));
    }
});