<commit_before>// interface_common.js
// 2013 <NAME>, <NAME>
//
// Common interface functions to all pages, modal manipulation, button manipulation etc
// Profile, mentions and hashtag modal
// Post actions: submit, count characters

var twister = {
    html: {
        detached: $('<div>'),  // here elements go to detach themself
        blanka: $('<a target="_blank">')  // to open stuff in new tab, see routeOnClick()
    },
    tmpl: {  // templates pointers are stored here
        root: $('<div>')  // templates should be detached from DOM and attached here; use extractTemplate()
    },
    modal: {}
};
var window_scrollY = 0;
var _watchHashChangeRelaxDontDoIt = window.location.hash === '' ? true : false;

// FIXME so looks like it's wrapper over $; it's here to select and manipulate detached elements too
// and actually I'm talking about 'so called \'detached\'' elements which appended to twister.html.detached
// we may just append twister.html.detached to document instead and remove this weird shit (or I need to
// improve my google skills to find native jQuery way to dig through all detached elemets with one query)
function getElem(req) {
    var elem = $(req);
    var h = twister.html.detached.find(req);

    for (var i = 0; i < h.length; i++)
        elem[elem.length++] = h[i];

    return elem;
}

function openModal(modal) {
    if (!modal.classBase) {
        modal.classBase = '.modal-wrapper';

        window_scrollY = window.pageYOffset;
        $('body').css('overflow', 'hidden');
    }

    if (modal.classBase !== '.prompt-wrapper')
        closeModal($(modal.classBase + ':not(#templates *)'), true);

    modal.self = $('#templates ' + modal.classBase).clone(true)
        .addClass(modal.classAdd);

    if (modal.title)
        modal.self.find('.modal-header h3').html(modal.title);
    if (modal.content)
        modal.content = modal.self.find('.modal-content')
            .append(modal.content);
    else
        modal.content = modal.self.find('.modal-content');

    modal.self.appendTo('body').fadeIn('fast');  // FIXME maybe it's better to append it to some container inside body

    if (modal.classBase === '.modal-wrapper') {
        twister.modal[window.location.hash] = modal;
        modal.self.attr('data-modal-id', window.location.hash);

        modal.drapper = $('<div>').appendTo(twister.html.detached);  // here modal goes instead detaching

        modal.content.outerHeight(modal.self.height() - modal.self.find('.modal-header').outerHeight());

        var windowHeight = $(window).height();
        if (modal.self.outerHeight() > windowHeight) {
            modal.content.outerHeight(modal.content.outerHeight() - modal.self.outerHeight() + windowHeight);
            modal.self.outerHeight(windowHeight);
            modal.self.css('margin-top', - windowHeight / 2);
        }
    }

    return modal;
}

function closeModal(req, switchMode) {
    if (typeof req === 'undefined')
        var elem = $('.modal-wrapper:not(#templates *)');  // select active modal(s)
    else if (req.jquery)
        var elem = req;
    else if (req.target)
        var elem = getElem(req.target);  // getElem() to search in minimized too
    else if (typeof req === 'string' || req.outerHTML)
        var elem = getElem(req);

    if (!elem || !elem.length)
        return;

    // we close all modals which are containing element(s)
    elem.closest('.modal-wrapper:not(.closed)').addClass('closed')
        .fadeOut(switchMode ? 10 : 'fast', function () {
            var i = this.getAttribute('data-modal-id');

            if (twister.modal[i].minimized)
                twister.modal[i].btnResume.fadeOut('fast', function () {this.remove();});
            else
                this.remove();  // if it's minimized it will be removed with twister.modal[i].drapper

            twister.modal[i].drapper.remove();
            twister.modal[i] = undefined;
        }
    );

    if (!switchMode) {
        if (window.location.hash !== '') {
            _watchHashChangeRelaxDontDoIt = true;
            window.location.hash = '#';
        }
        window.scroll(window.pageXOffset, window_scrollY);
        $('body').css({
            'overflow': 'auto',
            'margin-right': '0'
        });
    }
}

function closePrompt(req) {
    if (typeof req === 'undefined')
        var elem = $('.prompt-wrapper:not(#templates *)');
    else if (req.jquery)
        var elem = req;
    else if (req.target)
        var elem = $(req.target);
    else if (typeof req === 'string' || req.outerHTML)
        var elem = $(req);

    if (!elem || !elem.length)
        return;

    if (typeof req.stopPropagation === 'function') {
        req.preventDefault();
        req.stopPropagation();
        req = req.data;
    }

    // we close all prompts which are containing element(s)
    elem.closest('.prompt-wrapper:not(.closed)').addClass('closed')
        .fadeOut('fast', function() {this.remove();});

    if (req && typeof req.cbFunc === 'function')  // FIXME maybe bind to ^ prompt fadeout function
        req.cbFunc(req.cbReq);
}

function minimizeModal(modal, switchMode) {

    function minimize(modal, scroll) {
        var i = modal.attr('data-modal-id');

        modal.appendTo(twister.modal[i].drapper);

        twister.modal[i].minimized = true;
        twister.modal[i].scroll = scroll;
        twister.modal[i].btnResume = $('<li>' + modal.find('.modal-header h3').text() + '</li>')
            .on('click', {hashString: window.location.hash}, function (event) {
                if (event.button === 0)  // click may be catched not only on left mouse button in some browsers
                    resumeModal(event);
            })
            .on('mouseup', {route: window.location.hash, blankOnly: true}, routeOnClick)
            .appendTo($('#modals-minimized'))
        ;
    }

    if (modal.is('.closed')) return;

    var scroll;  // MUST be setted before modal.detach(), modal.fadeOut() and so on
    if (modal.is('.directMessages') || modal.is('.group-messages-new-group')
        || modal.is('.group-messages-join-group') || modal.is('.following-own-modal')) {
            scroll = {
                targetSelector: '.modal-content',
                top: modal.find('.modal-content').scrollTop()
            };
    } else if (modal.is('.profile-modal')) {
        if (modal.find('.profile-card').attr('data-screen-name')[0] === '*')
            scroll = {
                targetSelector: '.modal-content .members',
                top: modal.find('.modal-content .members').scrollTop()
            };
        else
            scroll = {
                targetSelector: '.modal-content .postboard-posts',
                top: modal.find('.modal-content .postboard-posts').scrollTop()
            };
    }

    if (switchMode)
        minimize(modal, scroll);
    else
        modal.fadeOut('fast', function () {
            minimize(modal, scroll);

            _watchHashChangeRelaxDontDoIt = true;
            window.location.hash = '#';
            window.scroll(window.pageXOffset, window_scrollY);
            $('body').css({
                'overflow': 'auto',
                'margin-right': '0'
            });
        });
}
<commit_msg>fix modals' scroll on resuming<commit_after>// interface_common.js
// 2013 <NAME>, <NAME>
//
// Common interface functions to all pages, modal manipulation, button manipulation etc
// Profile, mentions and hashtag modal
// Post actions: submit, count characters

var twister = {
    html: {
        detached: $('<div>'),  // here elements go to detach themself
        blanka: $('<a target="_blank">')  // to open stuff in new tab, see routeOnClick()
    },
    tmpl: {  // templates pointers are stored here
        root: $('<div>')  // templates should be detached from DOM and attached here; use extractTemplate()
    },
    modal: {}
};
var window_scrollY = 0;
var _watchHashChangeRelaxDontDoIt = window.location.hash === '' ? true : false;

// FIXME so looks like it's wrapper over $; it's here to select and manipulate detached elements too
// and actually I'm talking about 'so called \'detached\'' elements which appended to twister.html.detached
// we may just append twister.html.detached to document instead and remove this weird shit (or I need to
// improve my google skills to find native jQuery way to dig through all detached elemets with one query)
function getElem(req) {
    var elem = $(req);
    var h = twister.html.detached.find(req);

    for (var i = 0; i < h.length; i++)
        elem[elem.length++] = h[i];

    return elem;
}

function openModal(modal) {
    if (!modal.classBase) {
        modal.classBase = '.modal-wrapper';

        window_scrollY = window.pageYOffset;
        $('body').css('overflow', 'hidden');
    }

    if (modal.classBase !== '.prompt-wrapper')
        closeModal($(modal.classBase + ':not(#templates *)'), true);

    modal.self = $('#templates ' + modal.classBase).clone(true)
        .addClass(modal.classAdd);

    if (modal.title)
        modal.self.find('.modal-header h3').html(modal.title);
    if (modal.content)
        modal.content = modal.self.find('.modal-content')
            .append(modal.content);
    else
        modal.content = modal.self.find('.modal-content');

    modal.self.appendTo('body').fadeIn('fast');  // FIXME maybe it's better to append it to some container inside body

    if (modal.classBase === '.modal-wrapper') {
        twister.modal[window.location.hash] = modal;
        modal.self.attr('data-modal-id', window.location.hash);

        modal.drapper = $('<div>').appendTo(twister.html.detached);  // here modal goes instead detaching

        modal.content.outerHeight(modal.self.height() - modal.self.find('.modal-header').outerHeight());

        var windowHeight = $(window).height();
        if (modal.self.outerHeight() > windowHeight) {
            modal.content.outerHeight(modal.content.outerHeight() - modal.self.outerHeight() + windowHeight);
            modal.self.outerHeight(windowHeight);
            modal.self.css('margin-top', - windowHeight / 2);
        }
    }

    return modal;
}

function closeModal(req, switchMode) {
    if (typeof req === 'undefined')
        var elem = $('.modal-wrapper:not(#templates *)');  // select active modal(s)
    else if (req.jquery)
        var elem = req;
    else if (req.target)
        var elem = getElem(req.target);  // getElem() to search in minimized too
    else if (typeof req === 'string' || req.outerHTML)
        var elem = getElem(req);

    if (!elem || !elem.length)
        return;

    // we close all modals which are containing element(s)
    elem.closest('.modal-wrapper:not(.closed)').addClass('closed')
        .fadeOut(switchMode ? 10 : 'fast', function () {
            var i = this.getAttribute('data-modal-id');

            if (twister.modal[i].minimized)
                twister.modal[i].btnResume.fadeOut('fast', function () {this.remove();});
            else
                this.remove();  // if it's minimized it will be removed with twister.modal[i].drapper

            twister.modal[i].drapper.remove();
            twister.modal[i] = undefined;
        }
    );

    if (!switchMode) {
        if (window.location.hash !== '') {
            _watchHashChangeRelaxDontDoIt = true;
            window.location.hash = '#';
        }
        window.scroll(window.pageXOffset, window_scrollY);
        $('body').css({
            'overflow': 'auto',
            'margin-right': '0'
        });
    }
}

function closePrompt(req) {
    if (typeof req === 'undefined')
        var elem = $('.prompt-wrapper:not(#templates *)');
    else if (req.jquery)
        var elem = req;
    else if (req.target)
        var elem = $(req.target);
    else if (typeof req === 'string' || req.outerHTML)
        var elem = $(req);

    if (!elem || !elem.length)
        return;

    if (typeof req.stopPropagation === 'function') {
        req.preventDefault();
        req.stopPropagation();
        req = req.data;
    }

    // we close all prompts which are containing element(s)
    elem.closest('.prompt-wrapper:not(.closed)').addClass('closed')
        .fadeOut('fast', function() {this.remove();});

    if (req && typeof req.cbFunc === 'function')  // FIXME maybe bind to ^ prompt fadeout function
        req.cbFunc(req.cbReq);
}

function minimizeModal(modal, switchMode) {

    function minimize(modal, scroll) {
        var i = modal.attr('data-modal-id');

        modal.appendTo(twister.modal[i].drapper);

        twister.modal[i].minimized = true;
        twister.modal[i].scroll = scroll;
        twister.modal[i].btnResume = $('<li>' + modal.find('.modal-header h3').text() + '</li>')
            .on('click', {hashString: window.location.hash}, function (event) {
                if (event.button === 0)  // click may be catched not only on left mouse button in some browsers
                    resumeModal(event);
            })
            .on('mouseup', {route: window.location.hash, blankOnly: true}, routeOnClick)
            .appendTo($('#modals-minimized'))
        ;
    }

    if (modal.is('.closed')) return;

    var scroll;  // MUST be setted before modal.detach(), modal.fadeOut() and so on
    if (modal.is('.profile-modal')) {
        if (modal.find('.profile-card').attr('data-screen-name')[0] === '*')
            scroll = {
                targetSelector: '.modal-content .members',
                top: modal.find('.modal-content .members').scrollTop()
            };
        else
            scroll = {
                targetSelector: '.modal-content .postboard-posts',
                top: modal.find('.modal-content .postboard-posts').scrollTop()
            };
    } else
        scroll = {
            targetSelector: '.modal-content',
            top: modal.find('.modal-content').scrollTop()
        };

    if (switchMode)
        minimize(modal, scroll);
    else
        modal.fadeOut('fast', function () {
            minimize(modal, scroll);

            _watchHashChangeRelaxDontDoIt = true;
            window.location.hash = '#';
            window.scroll(window.pageXOffset, window_scrollY);
            $('body').css({
                'overflow': 'auto',
                'margin-right': '0'
            });
        });
}

{'fraction_non_alphanumeric': 0.1521906764808973, 'fraction_numerical': 0.002103049421661409, 'mean_word_length': 2.3355155482815055, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 2, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7020911', 'n_tokens_mistral': 4209, 'n_tokens_neox': 4049, 'n_words': 1011}