Friends.ActionsControl = new function() {
    this.createFromTag = function(element, params) {
        return new Friends.ActionsControl.Constructor(element, params);
    };
};

Friends.ActionsControl.Constructor = function(element, params) {
    /** Элемент контрола. */
    this.control = element;
    this.control.control = this;

    /** Форма комментария. */
    this.form = 0;

    /** Список действий. */
    this.actions = 0;

    /** Список параметров действий. */
    this.params = [];

    /** Индекс текущего выбраного действия. */
    this.action = 0;

    /** Кнопка показа/скрытия выпадающего списка. */
    this.button = 0;

    /** Общие для всего контрола параметры */
    this.commonParams = params;

    /** Флаг процесса сохранения, используется, чтобы не допустить дублирования комментов */
    this.savingInProcess = false;

    /** Флаг того, что контрол полностью подготовлен к работе */
    this.isPrepared = false;

    /** div-контейнер для выпадающих меню, необходим, чтобы решить проблемы с позиционированием. */
    /** Выпадающие меню помещаем в div, а не в body, потому что в opera тормозит body.appendChild, */
    /** если в body много элементов */
    this.actionsMenusContainer = y5.$('actionsMenusContainer');
    if (!this.actionsMenusContainer) {
        this.actionsMenusContainer = document.createElement('div');
        document.body.appendChild(this.actionsMenusContainer);
        this.actionsMenusContainer.id = 'actionsMenusContainer';
    }

    this.init();
    if (!params.is_reply) {
        Friends.ActionsControl.LAST_CREATED_INSTANCE = this;
    }
};


/**
 * Обработчик контрола "действия". У контрола есть одно действие по умолчанию
 * и несколько альтернативных действий, которые могу быть выбраны из выпадающего списка,
 * вызываемого нажатием на кнопку "или...". После выбора действия показывается форма действия.
 * Форма может быть скрыта повторным выбором действия.
 *
 * Структура контрола:
 *
 * <pre>
 *  <div class="b-actions">
 *      <div class="b-actions-list">
 *          <a href="" onclick="return ['text']">label1</a>
 *          <a class="or" href=""><i>или</i></a>
 *      </div>
 *
 *      <div class="b-actions-select b-actions-select-posts">
 *          <div class="holster">
 *              <div><a href="" onclick="return ['text']">label1</a></div>
 *              <div><a href="" onclick="return ['unfriend']">label2</a></div>
 *              <div><a href="" onclick="return ['post']">label3</a></div>
 *          </div>
            <a class="or" href=""><i>или</i></a>
 *      </div>
 *  </div>
 * </pre>
 *
 * Значение атрибута onclick вычисляется и передаётся в ассоциированый с этим
 * объектом onclick обработчик. Действия составляют выпадающий спискок. Ссылки с классом "or"
 * это кнопки "показать/спрятать выпадающий список".
 */
Friends.ActionsControl.Constructor.prototype = new function()
{
//public static
    this.TAGNAME = "code";
    this.CLASSNAME = "c-actions";

//public
    /**
     * Обрабатывает событие "нажатие на ссылку". В результате обработки форма
     * действия показывается или скрывается.
     *
     *  @param  _action элемент (ссылка) текущего действия
     */
    this.click = function(_index)
    {
        if (!this.isPrepared) {
            this.prepare();
        }

        if (y5.Types.array(_index)) {
            _index = this.actionsTypes[[_index[0]]];
        } else 
        if (typeof _index == "string") {
            _index = this.actionsTypes[_index];
        }   
        return this.showHideForm(_index);
    };

    this.keydown = function(_fake1, _fake2, e)
    {
        if (!e)
            e=window.event;
        var key=e.keyCode;

        if (key==27)
            this.hideForm();

        if (e.ctrlKey && (key==13))
            alert("submit");
    };

    this.showCommentLinks = function() {
        //при свернутой форме комментария ссылка рядом с "или" всегда должна быть та, что первая в списке
        var currentLinkParent = this.currentLink.parentNode;
        currentLinkParent.removeChild(this.currentLink);
        this.currentLink = currentLinkParent.insertBefore(this.initLink, this.button);
        y5.Classes.remove(this.currentLink, "pressed");
        if (this.button) {
            this.button.style.visibility = 'visible';
        }
    }

    this.hideForm = function()
    {
        this.showCommentLinks();

        this.closeCallback.stop();
        y5.Events.notify('y5:form_destructed', this.form);
        y5.Dom.removeNode(this.form);
        this.redrawRepliesContainer();
    };

    /**
     * Обрабатывает событие "нажатие на ссылку". В результате обработки форма
     * действия показывактся или скрывается.
     *
     *  @param  _userpic    userpic на который кликнули
     */
    this.selectUserpic = function(_userpic, _param)
    {
        /*
        if (y5.Classes.test(_userpic, 'fake')) {
            return;
        }
        */

        var selectedSpan = y5.Dom.getDescendant(_param.form, 'span', 'selected');
        if (selectedSpan) {
            y5.Classes.remove(selectedSpan, "selected");
        }
        y5.Classes.add(_userpic, "selected");

        // Сохраняем на форму текущий выбраный userpic
        var userpic = getById("userpic_id");
        userpic.value = _param.name;
        this.userpic = _userpic;
    };

    this.showAnonymousForm = function(_fake, _params)
    {
        var forms=getChildrenByTagName(_params[0], "div");
        for (var i=0, l=forms.length; i<l; i++)
            forms[i].style.display=(i==_params[1])?"block":"none";
    };

    this.setOpenIdType = function(_select, _types)
    {
        getNextElement(_select.parentNode, "div").firstChild.innerHTML=
            _types[_select.selectedIndex];
    };

//private
    /**
     * Показывает/скрывает форму быстрого действия. В качестве шаблона формы
     * быстрого действия используется элемент "form-"+_type, который клонируется
     * и добавляется на страницу. При скрытии формы он удаляется.
     *
     *  @param  _action элемент (ссылка) текущего действия
     *  @param  _params массив параметров
     */
    this.showHideForm = function(_action)
    {
        y5.Events.notify('y5:ActionsControl:syncTextModes', Friends.ActionsControl, true, {caller: this});
        // save body form
        var text = this.commonParams.body || '';
        if (document.forms['post'] && document.forms['post'].elements['body']) {
            text = document.forms['post'].elements['body'].value;
        }

        if (!y5.Classes.test(this.currentLink, "pressed")) {
            this.showForm(_action);
        } else if (this.action == _action) {
            this.hideForm();
        } else {
            this.hideForm();
            this.showForm(_action);
        }

        // restore body form
        if (document.forms['post'] && text != '') {
            document.forms['post'].elements['body'].value = text;
        }

        return false;
    };

    this.showForm = function(_action)
    {
        y5.Events.notify('y5:closeActionControl', y5.Dom.getBody(), true);

        y5.Classes.add(this.currentLink, "pressed");
        if (this.button) {
            this.button.style.visibility = 'hidden';
        }
        this.action = _action;

        this.createForm(this.getParent(), _action);

        this.closeCallback.start();

        this.redrawRepliesContainer();
    };

    // в ie на странице одного поста replies.xml без этого хака разъезжается верстка
    // при показе/скрытии формы комментария
    this.redrawRepliesContainer = function() {
        if (y5.is_ie && this.repliesContainer) {
            y5.Classes.add(this.repliesContainer, 'g-hidden');
            y5.Classes.remove(this.repliesContainer, 'g-hidden');
        }
    };

    this.getParent = function() {
        return y5.Dom.getAncestorOrSelf(this.control, '*', 'b-reply')  ||
               y5.Dom.getAncestorOrSelf(this.control, '*', 'head')     ||
               y5.Dom.getAncestorOrSelf(this.control, '*', 'b-post')   ||
               y5.Dom.getAncestorOrSelf(this.control, '*', 'b-action') ||
               this.control.parentNode;
    };

    /**
     * Показывает выпадающий список действий.
     */
    this.showActions = function(e, withDefaultFocus)
    {
        e.cancelBubble = true;
        this.adjustActionsMenu(null, withDefaultFocus);

        return false;
    };

    this.createUserpics = function()
    {
        // по наличию qu опеделяем, зарегистрирован ли пользователь на Я.ру
        if (g_source && (g_source.status == 'normal')) {
            var userpics = '' +
                '<div class="b-choose-userpic b-choose-userpic-reply Friends-c-ChooseUserpicControlCached">' +
                    '<table>';

            var userpicsLength = g_source.hasUserpics() ? g_source.getUserpicsCount() : 1;
            for (var i = 0, l = userpicsLength; i < l; i++) {
                userpics += '' +
                    '<tr' + ((i == 0) ? ' class="first"' : '') + '>'+
                        '<td><input id="userpic-' + (i + 1) + '" name="userpic" type="radio" value="' + g_source.getUserpicName(i) + '"' + ((i == g_source.currentUserpicId) ? ' checked="checked"' : '') + '/></td>' +
                        '<td class="image"><label for="userpic-' + (i + 1) + '">' + g_source.getUserpicContent({id:i, size: "middle", loaded: false}) + '</label></td>'+
                    '</tr>';
            }


            userpics += '</table></div>';

            return userpics;
        }

        return "";
    };

    this.createMenu = function()
    {
        var menu = document.createElement("ul");

        for (var i = 0, l = this.actions.length; i < l; i++)
        {
            var li = document.createElement("li");
            var inner;

            if (this.action == i) {
                inner = document.createElement("b");
                var currentLinkParent = this.currentLink.parentNode;
                currentLinkParent.removeChild(this.currentLink);
                var clone = this.actions[i].cloneNode(true);
                clone.onclick = this.actions[i].onclick;
                this.currentLink = currentLinkParent.insertBefore(clone, this.button);
                y5.Classes.add(this.currentLink, "pressed");

                // в IE при клонировании элемента события, навешанные через attachEvent,
                // тоже клонируются, поэтому для IE не навешиваем
                if (!y5.is_ie) {
                    this.addClickListener(this.currentLink);
                }
            }
            else
            {
                inner = document.createElement("a");
                inner.href = this.actions[i].href;
                inner.onclick = this.actions[i].onclick;
                inner.setAttribute("tabindex", 2);
                this.addClickListener(inner);
            }

            inner.innerHTML = this.actions[i].innerHTML;
            li.appendChild(inner);
            menu.appendChild(li);
        }

        return menu;
    };

    this.createAnonymousMenu = function()
    {
        var menu=document.createElement("div");
        menu.className="forms";

        menu.innerHTML=''+
            '<div>'+
                '<p>Вы не представились.</p>'+
                '<p><a>Войдите</a>, указав свой логин в Яндекс.Друзьях</p>'+
                '<p>или <a>укажите свой OpenID</a>, если вы из LiveJournal, '+
                    'LiveInternet или других блогов.</p>'+
            '</div>'+
            '<div>'+
                '<p>Представьтесь, пожалуйста:</p>'+
                '<div><span>Логин:</span> <input name="login"/></div>'+
                '<div><span>Пароль:</span> <input name="passwd"/></div>'+
                '<div>'+
                    '<label for="q1q">'+
                        '<input id="q1q" class="checkbox" name="twoweeks" type="checkbox"/> '+
                        'запомнить меня'+
                    '</label>'+
                '</div>'+
                '<p><a>спрятать форму</a></p>'+
            '</div>'+
            '<div>'+
                '<p>Представьтесь, пожалуйста:</p>'+
                '<div><span>Вы из</span> '+
                    '<select name="blog">'+
                        '<option value="lj">LiveJournal</option>'+
                        '<option value="li">LiveInternet</option>'+
                        '<option value="other">другой блог</option>'+
                    '</select>'+
                '</div>'+
                '<div><span>и ваше имя:</span> <input name="openid"/></div>'+
                '<p><a>спрятать форму</a></p>'+
            '</div>';

        var a=menu.getElementsByTagName("a");
        a[0].onclick=associateEventWithObject(this, "showAnonymousForm", [menu, 1]);
        a[1].onclick=associateEventWithObject(this, "showAnonymousForm", [menu, 2]);
        a[2].onclick=a[3].onclick=associateEventWithObject(
                this, "showAnonymousForm", [menu, 0]);

        menu.getElementsByTagName("select")[0].onchange=
                associateEventWithObject(this, "setOpenIdType",
                        ["и ваше имя:", "и ваше имя:", "URL вашего блога:"]);

        this.showAnonymousForm(null, [menu, 0]);

        return menu;
    };

    this.getBtnsHTML = function(okBtnText, cancelBtnText) {
        okBtnText = okBtnText || ' OK ';
        cancelBtnText = cancelBtnText || 'Отменить';
        var res;
        var transparent_checkbox = this.commonParams.transparent ?
                '<div class="options"><input type="checkbox" checked="checked" name="welcome_to_the_club" id="welcome_to_the_club" value="yes"/><label for="welcome_to_the_club">и вступить в клуб</label></div>' :
               '';

        if (transparent_checkbox) {
            res = y5.T(transparent_checkbox +
                '<b class="b-note b-note-attention" style="visibility: hidden;">Форма не заполнена</b>' +
                '<div class="b-loading g-hidden"><i>&#0160;</i></div>' +
                '<div class="buttons">'+
                    '<input class="submit js-submit" type="submit" tabindex="1" accesskey="s" value="#{1}" onclick="this.blur();" /> '+
                    '<input class="submit js-cancel" type="button" tabindex="1" accesskey="c" value="#{2}" /> '+
                '</div>',
            okBtnText, cancelBtnText);
        } else {
            res = y5.T('<b class="b-note b-note-attention" style="visibility: hidden;">Форма не заполнена</b>' +
                '<div class="b-loading g-hidden"><i>&#0160;</i></div>' +
                '<input class="submit js-submit" type="submit" tabindex="1" accesskey="s" value="#{1}" onclick="this.blur();" /> '+
                '<input class="submit js-cancel" type="button" tabindex="1" accesskey="c" value="#{2}" /> ',
            okBtnText, cancelBtnText);
        }

        return res;
    };

    this.getRecordOwner = function (params, author) {
        var result = '';
        if (g_source && params.author_id == g_source.id) {
            result = 'свою запись';
        } else if (params.record_is_anonymous) {
            result = 'запись анонима';
        } else {
            result = 'запись ' + author.yauser();
        }
        return result;
    };

    this.createForm = function(_reply, _action) {
        y5.Events.notify('y5:ActionsControl:reset', Friends.ActionsControl, true);
        // TODO: vitaly: переименовать _params
        var _params = this.params[_action];

        // параметры (только общие для всех типов, специфические ниже для каждого типа)
        var params = {
            source: this.commonParams.source,
            item_no: this.commonParams.item_no,
            parent_id: this.commonParams.parent_id ? this.commonParams.parent_id : '0',
            author_id: this.commonParams.author_id,
            author_login: this.commonParams.author_login,
            author_title: this.commonParams.author_title,
            author_qu: this.commonParams.author_qu,
            access: this.commonParams.access,
            type: _params[0],
            record_is_anonymous: this.commonParams.record_is_anonymous,
            // запись, которую комментируем, является комментом
            is_reply: this.commonParams.is_reply,
            // номер поста, к которому идут комменты (использовать item_no не можем,
            // поскольку в случае трекбечного коммента он содержит номер трекбечного  поста)
            post_id: this.commonParams.post_id,
            is_in_private_club: this.commonParams.is_in_private_club
        };

        var author = g_sources.addSource(
            {
                id : params.author_id,
                login : params.author_login,
                title : params.author_title,
                qu : params.author_qu
            }
        );
        var formCreated = false;

        // только(!) для ИЕ надо создавать name и enctype в момент создания элемента.
        if (y5.is_ie) {
            // подстраховываемся на случай, если в каком-то IE такое создание не прокатит
            try {
                this.form = document.createElement('<form name="post" enctype="multipart/form-data">');
                formCreated = true;
            } catch(e) { }
        }

        if (!formCreated) {
            this.form = document.createElement('form');
            this.form.name = 'post';
            this.form.enctype = 'multipart/form-data';
        }
        this.form.method = 'post';

        //this.form.onkeypress = null; // TODO: https://jira.yandex.ru/browse/BLOG-166

        var className = params.type;
        this.formType = params.type;

        // FireFox ведёт себя с form.action не как со строкой, а исходя из контекста формы
        // поэтому будем собирать action в отдельной переменной
        var formAction = friendsURL('replies_do_add');
        var needs_trackback = (!params.is_in_private_club) && (['text', 'post', 'complaint', 'subscribe', 'ban_and_delete'].indexOf(params.type) == -1);

        var formsCommon = '<input type="hidden" name="type" value="' + params.type + '"/>' +
                          '<input type="hidden" name="item_no" value="' + params.item_no + '"/>' +
                          '<input type="hidden" name="parent_id" value="' + params.parent_id + '"/>' +
                          '<input type="hidden" name="host_id" value="' + params.source.id + '"/>' +
                          '<input type="hidden" name="trackback" value="' + (needs_trackback ? "1" : "0") + '"/>'+
                          '<input type="hidden" name="host" value="' + params.source.id + '.id."/>';


//        var menu=g_source?this.createMenu():this.createAnonymousMenu();
        var menu = this.createMenu();
        this.menu = menu;
        var userpics = this.createUserpics();
        var form_open=
            '<table class="actions">'+
                '<colgroup>'+
                    '<col width="16%"/>'+
                    '<col width="50%"/>'+
                    '<col width="34%"/>'+
                '</colgroup>'+
                '<tr>'+
                    '<td class="userpics"></td>'+
                    '<td class="text"></td>'+
                    '<td class="menu"></td>'+
                '</tr>';
        var form_close='</table>';

        if (params.type == "text")
        {
            var record = this.getRecordOwner(params, author);

            this.form.innerHTML=form_open+
                '<tr>'+
                    '<td></td>'+
                    '<th colspan="2">Прокомментировать ' + record + '</th>'+
                '</tr>'+
                '<tr>'+
                    '<td rowspan="2" class="userpics">'+userpics+'</td>'+
                    '<td class="text">'+
                        '<div class="data Friends-c-W5GComments b-w5g b-w5g-comment">'+
                            '<textarea name="body" cols="40" rows="8" tabindex="1"></textarea>'+
                        '</div>'+
                    '</td>'+
                    '<td class="b-actions-menu" rowspan="2"></td>'+
                '</tr>'+
                '<tr>'+
                    '<td class="action">'+
                        this.getBtnsHTML() + formsCommon +
                    '</td>'+
                '</tr>'+
            form_close;

        }
        else if (params.type == 'link')
        {
            var url = '',
                src = '',
                title = '';

            var force_cut = false,
                postContent,
                postHeightCurrent,
                cuts = [],
                root_cuts = [],
                _cut;

            var post = y5.$('post-' + params.post_id);

            if (y5.Classes.test(post, 'b-post-link')) {
                try {
                    url = y5.cssQuery('.quote h4 a', post)[0];

                    title = url.innerHTML;
                    url = url.href;
                } catch (e) {}
            } else {
                try {
                    title = y5.cssQuery('.b-message-head h4 a', post)[0].innerHTML;
                } catch (e) {
                    try {
                        title = y5.cssQuery('.b-message-head h4', post)[0].innerHTML;
                    } catch (e) {
                        try {
                            title = y5.cssQuery('.data-row > .type > .b-title h4', post)[0].innerHTML;
                        } catch (e) {
                        }
                    }
                }

                url = friendsFullURL(params.source, 'replies', 'item_no=' + params.item_no);
            }
            url = y5.Strings.escapeHTML(url);
            title = y5.Strings.escapeHTML(title);
            postContent = y5.Dom.getDescendant(post, 'div', 'post-content');

            onclickToSrc(postContent);

            force_cut = postContent.offsetHeight > 350;
            src = y5.Strings.escapeHTML(postContent.innerHTML);

            if (force_cut) {
                src = '<cut>' + src + '</cut>';
            }

            var isPrivatePost = (params.access && params.access != 'public'); // подзамочный пост
            var isActivityVideoPost = y5.Classes.test(post, 'b-post-activity_video');
            var isActivityFotkiPost = y5.Classes.test(post, 'b-post-activity_fotki');

            var quote_html;

            // подзамочные посты цитировать нельзя
            if (isPrivatePost) {
                quote_html = '<div class="b-quote-source not-public">'+
                        'цитировать источник нельзя, т.к. доступ к нему ограничен</div>';
            }
            // для комментов и activity-постов блок цитирования не выводим
            else if (params.is_reply || isActivityVideoPost || isActivityFotkiPost) {
                quote_html = '';
            } else {
                quote_html = '<div class="b-quote-source">'+
                        '<input id="q-s" type="checkbox" name="source" checked="checked" value="' + src + '"/>'+
                        '<label for="q-s">Цитировать источник</label>'+
                        (force_cut? '<span class="q-s-notification">под врезкой</span>' : '') +
                    '</div>';
            }

            this.form.innerHTML=form_open+
                '<tr>'+
                    '<td></td>'+
                    '<th colspan="2">Поделиться ссылкой с друзьями</th>'+
                '</tr>'+
                '<tr>'+
                    '<td rowspan="2" class="userpics">' + userpics + '</td>' +
                    '<td class="text">'+
                         quote_html +
                        '<div class="b-write-short-text data Friends-c-W5GComments b-w5g b-w5g-comment">'+
                            '<textarea name="body" cols="40" rows="8" tabindex="1"></textarea>'+
                        '</div>'+
                        '<div class="keywordsContainer">'+
                        '</div>'+
                    '</td>'+
                    '<td class="b-actions-menu"></td>'+
                '</tr>'+
                '<tr>'+
                    '<td class="action">'+
                        this.getBtnsHTML() + formsCommon +
                        '<input type="hidden" name="title" value="'+title+'"/>'+
                        '<input type="hidden" name="URL" value="'+url+'"/>'+
                    '</td>'+
                    '<td></td>'+
                '</tr>'+
            form_close;
            
            var _this = this;

            y5.require(['Ajax', 'AjaxJS', 'Dom'], function() {
                function responce(request) {
                    var cont = y5.Dom.getDescendant(_this.form, 'div', 'keywordsContainer');
                    cont.innerHTML = request.html;
                    y5.Events.observe(
                            'click',
                            function(e) {
                                y5.Classes.assign(y5.Dom.getDescendant(_this.form, 'span', 'q-s-notification'), 'g-hidden', !e.target.checked);
                            },
                            y5.$('q-s'),
                            true,
                            _this)
                    y5.Components.init(_this.form);
                }
                var ajax = new y5.Ajax(
                    new y5.AjaxJS(g_source.id),
                    friendsURL('ajax/keywords'),
                    responce,
                    y5.Vars.NULL,
                    {}
                );
                ajax.send();
            });
        }
        else if (params.type == "post")
        {
            var record = this.getRecordOwner(params, author);

            this.form.innerHTML=form_open+
                '<tr>'+
                    '<td></td>'+
                    '<th colspan="2">Написать у себя про ' + record + '</th>'+
                '</tr>'+
                '<tr>'+
                    '<td rowspan="2" class="userpics">'+userpics+'</td>'+
                    '<td class="text">'+
                        '<div class="data">'+
                            '<textarea name="body" cols="40" rows="15" tabindex="1"></textarea>'+
                        '</div>'+
                    '</td>'+
                    '<td class="b-actions-menu"></td>'+
                '</tr>'+
                '<tr>'+
                    '<td class="action">'+
                        this.getBtnsHTML() + formsCommon +
                    '</td>'+'<td></td>'+
                '</tr>'+
            form_close;
        }
        else if (params.type == "friend" || params.type == "friend_too")
        {
            var action_source = g_sources.addSource(_params[1]);

            this.form.innerHTML=form_open+
                '<tr>'+
                    '<td></td>'+
                    '<th colspan="2">В ответ подружиться с ' + action_source.yauser(true) + '</th>'+
                '</tr>'+
                '<tr>'+
                    '<td rowspan="2" class="userpics">'+userpics+'</td>'+
                    '<td class="text">'+
                        '<div class="data">'+
                            '<div class="userinfo">'+
                                '<div class="b-user-info b-user-info-short b-user-info-empty">'+
                                    '<img src="' + g_globals.img_base + 'spin.gif" alt=""/> Идёт загрузка карточки пользователя'+
                                '</div>'+
                            '</div>'+
                            '<span class="say">и сказать</span>'+
                            '<textarea name="body" cols="40" rows="2" tabindex="1"></textarea>'+
                        '</div>'+
                    '</td>'+
                    '<td class="b-actions-menu"></td>'+
                '</tr>'+
                '<tr>'+
                    '<td class="action">'+
                        this.getBtnsHTML() + formsCommon +
                        '<input type="hidden" name="user" value="'+action_source.id+'"/>'+
                    '</td>'+
                    '<td></td>'+
                '</tr>'+
            form_close;

            if (params.type == "friend_too") {
                className = 'friend';
                y5.cssQuery('input[name="type"]', this.form)[0].value = "friend";
            }
            var _this = this;

            // функция на успех запроса
            var responseOK = function(user, trace) {
                var info = y5.Dom.getDescendants(_this.form, 'div', 'userinfo');
                if (info.length > 0)
                    info[0].innerHTML = user.info;
            }

            // функция на неудачу запроса
            var responseError = function(trace) {
            }

            g_sources.getSource(action_source.id, responseOK, responseError, {parent: this}, true);
        }
        else if (params.type == "unfriend" || params.type == "unfriend_too")
        {
            var action_source;
            action_source = g_sources.addSource(_params[1]);

            this.form.innerHTML=form_open+
                '<tr>'+
                    '<td></td>'+
                    '<th colspan="2">В ответ поссориться с ' + action_source.yauser(true) + '</th>'+
                '</tr>'+
                '<tr>'+
                    '<td rowspan="2" class="userpics">'+userpics+'</td>'+
                    '<td class="text">'+
                        '<div class="data">'+
                            '<div class="userinfo">'+
                                '<div class="b-user-info b-user-info-short b-user-info-empty">'+
                                    '<img src="' + g_globals.img_base + 'spin.gif" alt=""/> Идёт загрузка карточки пользователя'+
                                '</div>'+
                            '</div>'+
                            '<span class="say">и сказать</span>'+
                            '<textarea name="body" cols="40" rows="2" tabindex="1"></textarea>'+
                        '</div>'+
                    '</td>'+
                    '<td class="b-actions-menu"></td>'+
                '</tr>'+
                '<tr>'+
                    '<td class="action">'+
                        this.getBtnsHTML() + formsCommon +
                        '<input type="hidden" name="user" value="'+action_source.id+'"/>'+
                    '</td>'+
                    '<td></td>'+
                '</tr>'+
            form_close;

            if (params.type == "unfriend_too") {
                className = 'unfriend';
                y5.cssQuery('input[name="type"]', this.form)[0].value = "unfriend";
            }

            _this = this;

            // функция на успех запроса
            var responseOK = function(user, trace) {
                var info = y5.Dom.getDescendants(_this.form, 'div', 'userinfo');
                if (info.length > 0)
                    info[0].innerHTML = user.info;
            }

            // функция на неудачу запроса
            var responseError = function(trace) {
            }

            g_sources.getSource(action_source.id, responseOK, responseError, {parent: this}, true);
        }
        else if (params.type == "status")
        {
            // специфичные для типа параметры
            params.old_value = _params[1];
            params.new_value = _params[2];

            var current_status = '';

            this.form.innerHTML=form_open+
                '<tr>'+
                    '<td></td>'+
                    '<th colspan="2">Сменить в ответ своё настроение</th>'+
                '</tr>'+
                '<tr>'+
                    '<td rowspan="2" class="userpics">'+userpics+'</td>'+
                    '<td class="text">'+
                        '<div class="data">'+
                            '<textarea name="body" cols="40" rows="4" tabindex="1">'+
                            current_status+
                            '</textarea>'+
                            '<p><span>Предыдущие:</span> '+
                                '<div class="last-status">'+
                                    '<select disabled="disabled"><option>Загрузка&#8230;</option></select>'+
                                '</div>'+
                            '</p>'+
                        '</div>'+
                    '</td>'+
                    '<td class="b-actions-menu"></td>'+
                '</tr>'+
                '<tr>'+
                    '<td class="action">'+
                        this.getBtnsHTML() + formsCommon +
                        '<input type="hidden" name="old" value="'+params.old_value+'"/>'+
                        '<input type="hidden" name="new" value="'+params.new_value+'"/>'+
                    '</td>'+
                    '<td></td>'+
                '</tr>'+
            form_close;

            var _this = this;

            y5.require(['Ajax', 'AjaxJS', 'Dom'], function() {
                function responce(request) {
                    var cont = y5.Dom.getDescendant(_this.form, 'div', 'last-status');
                    cont.innerHTML = request.html;
                    y5.Events.observe(
                        'click',
                        function(e) {
                            _this.textArea.value = y5.Dom.textContent(y5.Dom.getDescendantOrSelf(e.target, 'span', 'pseudo-link'));
                        },
                        cont,
                        true,
                        _this
                    )
                }

                var ajax = new y5.Ajax(
                    new y5.AjaxJS(g_source.id),
                    friendsURL('ajax/statuses'),
                    responce,
                    y5.Vars.NULL,
                    {}
                );
                ajax.send();

            });

        }
        else if (params.type == "slashme")
        {
            this.form.innerHTML=form_open+
                '<tr>'+
                    '<td></td>'+
                    '<th colspan="2">В ответ /me </th>'+
                '</tr>'+
                '<tr>'+
                    '<td rowspan="2" class="userpics">'+userpics+'</td>'+
                    '<td class="text">'+
                        '<div class="data">'+
                            '<input type="text" name="title" tabindex="1" value="'+
                            _params[1] +'"/>'+
                        '</div>'+
                    '</td>'+
                    '<td class="b-actions-menu"></td>'+
                '</tr>'+
                '<tr>'+
                    '<td class="action">'+
                        this.getBtnsHTML() + formsCommon +
                    '</td>'+
                    '<td></td>'+
                '</tr>'+
            form_close;
        }
        else if (params.type == "rename")
        {
            // специфичные для типа параметры
            params.old_value = _params[1];
            params.new_value = _params[2];

            var current_name = '';

            this.form.innerHTML=form_open+
                '<tr>'+
                    '<td></td>'+
                    '<th colspan="2">Сменить в ответ своё имя</th>'+
                '</tr>'+
                '<tr>'+
                    '<td rowspan="2" class="userpics">'+userpics+'</td>'+
                    '<td class="text">'+
                        '<div class="data">'+
                            '<textarea name="body" cols="40" rows="4" tabindex="1">'+
                            current_name+
                            '</textarea>'+
                            '<p><span>Предыдущие: </span> '+
                                '<div class="last-name">'+
                                    '<select disabled="disabled"><option>Загрузка&#8230;</option></select>'+
                                '</div>'+
                            '</p>'+
                        '</div>'+
                    '</td>'+
                    '<td class="b-actions-menu"></td>'+
                '</tr>'+
                '<tr>'+
                    '<td class="action">'+
                        this.getBtnsHTML() + formsCommon +
                        '<input type="hidden" name="old" value="'+params.old_value+'"/>'+
                        '<input type="hidden" name="new" value="'+params.new_value+'"/>'+
                    '</td>'+
                    '<td></td>'+
                '</tr>'+
            form_close;

            var _this = this;

            y5.require(['Ajax', 'AjaxJS', 'Dom'], function() {
                function responce(request) {
                    var cont = y5.Dom.getDescendant(_this.form, 'div', 'last-name');
                    cont.innerHTML = request.html;
                    y5.Events.observe(
                        'click',
                        function(e) {
                            _this.textArea.value = y5.Dom.textContent(y5.Dom.getDescendantOrSelf(e.target, 'span', 'pseudo-link'));
                        },
                        cont,
                        true,
                        _this
                    )
                }
                var ajax = new y5.Ajax(
                    new y5.AjaxJS(g_source.id),
                    friendsURL('ajax/titles'),
                    responce,
                    y5.Vars.NULL,
                    {}
                );
                ajax.send();
            });

        }
        else if (params.type == "userpic")
        {
            this.form.innerHTML=form_open+
                '<tr>'+
                    '<td></td>'+
                    '<th colspan="2">Сменить в ответ свою картинку</th>'+
                '</tr>'+
                '<tr>'+
                    '<td rowspan="4" class="userpics">'+userpics+'</td>'+
                    '<td class="text">'+
                        '<div class="data">'+
                            '<div class="userpics"></div>'+
                        '</div>'+
                    '</td>'+
                    '<td rowspan="3" class="b-actions-menu"></td>'+
                '</tr>'+
                '<tr>'+
                    '<td>'+
                        '<span class="say file">из файла</span>'+
                        '<table class="load"><tr>'+
                            '<td><span><input onchange="y5.Events.notify(\'y5:\' + y5.Utils.getUniqueId(this.form), this.form, true);" id="file_id" class="pictureloader-file browse" name="file" type="file" accept="image/*" size="15"/></span>'+
                            '<input name="file_name" id="file_name_id" class="pictureloader-filename" value="" type="hidden"/>'+
                            '<input name="retpage" value="' + friendsURL('posts_ret_add_userpic') + '" type="hidden"/>'+
                            '</td>'+
                            '<td class="gap"><div></div></td>'+
                            '<td class="new"></td>'+
                        '</tr></table>'+
                    '</td>'+
                '</tr>'+
                '<tr>'+
                    '<td>'+
                        '<span class="say">и сказать</span>'+
                        '<textarea name="body" cols="40" rows="2" tabindex="1"></textarea>'+
                    '</td>'+
                '</tr>'+
                '<tr>'+
                    '<td class="action">'+
                        this.getBtnsHTML() + formsCommon +
                        '<input name="user" value="'+g_source.login+'" type="hidden"/>'+
                        '<input name="userpic" id="userpic_id" value="" type="hidden"/>'+
                    '</td>'+
                    '<td></td>'+
                '</tr>'+
            form_close;

            var _this = this;
            var pics = y5.Dom.getDescendant(this.form, 'div', 'userpics');
            var fakePicSrc="http://img.yandex.ru/i/zero.gif";

            function createUserpic(src, name, id, id_block, selected, className)
            {
                var span = document.createElement("span");
                if(id_block) {
                    span.id = id_block;
                }
                span.innerHTML = g_source.getUserpicContent({id: id, size: "middle", ids: id, src: src, className: (className || null)});
                if (selected) {
                    span.className="selected";
                    _this.initUserpic = span;
                }
                span.onclick = associateEventWithObject(_this, "selectUserpic", {form: _this.form, name: name});
                pics.appendChild(span);
                return span;
            }

            var userpicsLength = g_source.hasUserpics() ? g_source.getUserpicsCount() : 1;
            for (var i=0, l=userpicsLength; i<l; i++)
            {
                var id="userpic-"+(i);
                createUserpic(
                    g_source.getUserpic({id: i, size: 'middle'}),
                    g_source.getUserpicName(i),
                    id,
                    '',
                    i == g_source.currentUserpicId
                );
            }

            var pics = y5.Dom.getDescendant(_this.form, 'td', 'new');
            var span = createUserpic(fakePicSrc, "", "userpic-new_image", "userpic-new", false, "pictureloader-image");
            //см. https://jira.yandex-team.ru/browse/BLOG-5338
            //домчик куда грузим потенциально новый юзерпик создаётся той же функцией, что и уже существующие юзерпики, из которых можно выбрать и которые, соответственно, можно кликнуть
            //TODO: убираем навешанный онклик, но вообще тут надо рефакторить
            span.onclick = function() { };

            y5.Classes.add(span, 'pictureloader-span fake');

            window.g_sizePicture = function()
            {
                return "middle";
            };

            // по загрузке инициализируется
            // возможность асинхронной загрузки картинки
            function initLoadAvatar() {

                // таблица с type="file" уходит на навешивание функционала
                var load = y5.Dom.getDescendant(_this.form, 'table', 'load');
                var span = y5.Dom.getDescendant(_this.form, 'span', 'pictureloader-span');
                var userpic_loader = new Friends.PictureLoaderAvatar(load, {size: 'middle', simpleForm:true});
                y5.Events.observe('y5:userpic_clicked', function() {
                        _this.selectUserpic(span, {form: _this.form, name: this.response.name});
                }, userpic_loader, true, userpic_loader);

                // ожидается собитие "картинка загрузилась"
                function onload(name) {
                    // сохранение имени картинки
                    var input = getById('userpic_id');
                    input.value = name;

                    y5.Classes.remove(span , 'fake');

                    // выбор загруженной картинки картинки
                    _this.selectUserpic(span, {form: _this.form, name: name});
                }
                // вешается обработчик события на загрузку картинки
                y5.Events.observe('y5:load', onload, load, true);
            }

            // загрузить необходимые части функционала
            y5.require(['Events', '{Friends}.PictureLoaderAvatar'], initLoadAvatar);
        }
        else if (params.type == "photo")
        {
            className = 'userpic';

            this.form.innerHTML=form_open+
                '<tr>'+
                    '<td></td>'+
                    '<th colspan="2">Вывесить в ответ свои фото</th>'+
                '</tr>'+
                '<tr>'+
                    '<td rowspan="4" class="photo">'+userpics+'</td>'+
                    '<td class="text">'+
                        '<div class="data">'+
                            '<div id="photo-holder" class="b-write-photo">'+
                            '</div>'+
                            '<div class="spin new" id="photo-new-id">'+
                            '</div>'+
                        '</div>'+
                    '</td>'+
                    '<td rowspan="3" class="b-actions-menu"></td>'+
                '</tr>'+
                '<tr>'+
                    '<td>'+
                        '<span class="say">из файла</span>'+
                        '<table class="load"><tr>'+
                            '<td><span><input onchange="y5.Events.notify(\'y5:\' + y5.Utils.getUniqueId(this.form), this.form, true);" id="file_id" class="pictureloader-file browse" name="file" type="file" accept="image/*" size="15"/></span>'+
                            '<input name="file_name" id="file_name_id" class="pictureloader-filename" value="" type="hidden"/>'+
                            '<input name="retpage" value="'+friendsURL('posts_ret_add_photo')+'" type="hidden"/>'+
                            '</td>'+
                        '</tr></table><br />'+
                    '</td>'+
                '</tr>'+
                '<tr>'+
                    '<td>'+
                        '<span class="say">и сказать</span>'+
                        '<textarea name="body" cols="40" rows="2" tabindex="1"></textarea>'+
                    '</td>'+
                '</tr>'+
                '<tr>'+
                    '<td class="action">'+
                        this.getBtnsHTML() + formsCommon +
                        '<input name="user" value="'+g_source.login+'" type="hidden"/>'+
                        '<input type="hidden" name="id_list_with_authors"/>'+
                        '<input type="hidden" name="access" value="private"/>'+
                    '</td>'+
                    '<td></td>'+
                '</tr>'+
            form_close;

            var _this = this;

            // по загрузке инициализируется
            // возможность асинхронной загрузки картинки
            function initLoadPicture() {

                // таблица с type="file" уходит на навешивание функционала
                var load = y5.Dom.getDescendant(_this.form, 'table', 'load');
                new Friends.PictureLoaderPhoto(load);

            }

            // загрузить необходимые части функционала
            y5.require(['Events', '{Friends}.PictureLoaderPhoto'], function() {
               window.setTimeout(initLoadPicture, 100);
            });
        } else if (params.type == "congratulation") {
            var action_source = g_sources.addSource(_params[1]);
            // кого поздравляем: себя, друга, пользователя
            var whom = action_source.yauser();

            this.form.innerHTML=form_open+
                '<tr>'+
                    '<td></td>'+
                    '<th colspan="2">В ответ поздравить ' + _params[2] + ' ' + whom + ' ' + _params[4] +'</th>'+
                '</tr>'+
                '</tr>'+
                '<tr>'+
                    '<td rowspan="2" class="userpics">'+userpics+'</td>'+
                    '<td class="text">'+
                        '<div class="data">'+
                            '<textarea name="body" cols="40" rows="8" tabindex="1"></textarea>'+
                        '</div>'+
                    '</td>'+
                    '<td class="b-actions-menu"></td>'+
                '</tr>'+
                '<tr>'+
                    '<td class="action">'+
                        this.getBtnsHTML() + formsCommon +
                        '<input type="hidden" name="event" value="'+ _params[3] + '"/>'+
                        '<input type="hidden" name="whom" value="'+action_source.id+'"/>'+
                    '</td>'+
                    '<td></td>'+
                '</tr>'+
            form_close;

        } else if (params.type == 'join') {
            //присоединение к клубу
            var action_source = g_sources.addSource(_params[1]);

            this.form.innerHTML=form_open+
                '<tr>'+
                    '<td></td>'+
                    '<th colspan="2">Присоединиться к клубу ' + action_source.yauser(false) + '</th>'+
                '</tr>'+
                '<tr>'+
                    '<td rowspan="2" class="userpics">'+userpics+'</td>'+
                    '<td class="text">'+
                        '<div class="data">'+
                            '<div class="userinfo">'+
                                '<div class="b-user-info b-user-info-short b-user-info-empty">'+
                                    '<img src="' + g_globals.img_base + 'spin.gif" alt=""/> Идёт загрузка карточки клуба'+
                                '</div>'+
                            '</div>'+
                            '<span class="say">и сказать</span>'+
                            '<textarea name="body" cols="40" rows="2" tabindex="1"></textarea>'+
                        '</div>'+
                    '</td>'+
                    '<td class="b-actions-menu"></td>'+
                '</tr>'+
                '<tr>'+
                    '<td class="action">'+
                        this.getBtnsHTML() + formsCommon +
                        '<input type="hidden" name="user" value="'+action_source.id+'"/>'+
                    '</td>'+
                    '<td></td>'+
                '</tr>'+
            form_close;

            _this = this;

            // функция на успех запроса
            var responseOK = function(user, trace) {
                var info = y5.Dom.getDescendants(_this.form, 'div', 'userinfo');
                if (info.length > 0)
                    info[0].innerHTML = user.info;
            }

            // функция на неудачу запроса
            var responseError = function(trace) {
            }
            g_sources.getSource(action_source.id, responseOK, responseError, {parent: this}, true);

        } else if (params.type == 'unjoin') {
            //уход из клуба
            var action_source = g_sources.addSource(_params[1]);

            this.form.innerHTML=form_open+
                '<tr>'+
                    '<td></td>'+
                    '<th colspan="2">Уйти из клуба ' + action_source.yauser(false) + '</th>'+
                '</tr>'+
                '<tr>'+
                    '<td rowspan="2" class="userpics">'+userpics+'</td>'+
                    '<td class="text">'+
                        '<div class="data">'+
                            '<div class="userinfo">'+
                                '<div class="b-user-info b-user-info-short b-user-info-empty">'+
                                    '<img src="' + g_globals.img_base + 'spin.gif" alt=""/> Идёт загрузка карточки клуба'+
                                '</div>'+
                            '</div>'+
                            '<span class="say">и сказать</span>'+
                            '<textarea name="body" cols="40" rows="2" tabindex="1"></textarea>'+
                        '</div>'+
                    '</td>'+
                    '<td class="b-actions-menu"></td>'+
                '</tr>'+
                '<tr>'+
                    '<td class="action">'+
                        this.getBtnsHTML() + formsCommon +
                        '<input type="hidden" name="user" value="'+action_source.id+'"/>'+
                    '</td>'+
                    '<td></td>'+
                '</tr>'+
            form_close;

            _this = this;

            // функция на успех запроса
            var responseOK = function(user, trace) {
                var info = y5.Dom.getDescendants(_this.form, 'div', 'userinfo');
                if (info.length > 0)
                    info[0].innerHTML = user.info;
            }

            // функция на неудачу запроса
            var responseError = function(trace) {
            }
            g_sources.getSource(action_source.id, responseOK, responseError, {parent: this}, true);

        } else if (params.type == 'complaint') {
            var record = this.getRecordOwner(params, author);
            this.form.innerHTML=form_open+
                '<tr>'+
                    '<td></td>'+
                    '<th colspan="2">Пожаловаться на '+ record  + '</th>'+
                '</tr>'+
                '<tr>'+
                    '<td rowspan="2" class="userpics">'+userpics+'</td>'+
                    '<td class="text">'+
                        '<div class="b-quote-source">'+
                            '<input id="anonymous" type="checkbox" name="anonymous"/>'+
                            '<label for="anonymous"> анонимно</label>'+
                        '</div>'+
                        '<div class="data">'+
                            '<textarea name="body" cols="40" rows="15" tabindex="1"></textarea>'+
                        '</div>'+
                    '</td>'+
                    '<td class="b-actions-menu"></td>'+
                '</tr>'+
                '<tr>'+
                    '<td colspan="2" class="action">'+
                        '<div class="buttons">' +
                            this.getBtnsHTML('Пожаловаться') + formsCommon +
                            '<input type="hidden" name="author_id" value="' + author.id + '"/>' +
                        '</div>' +
                    '</td>'+
                '</tr>'+
            form_close;
        } else if (params.type == 'subscribe') {
            var record = this.getRecordOwner(params, author);
            // Как-то получить аяксиком значение yes|no с ручки
            // ajax/post_is_subscribed.xml?feed_id=[feed_id]&item_no=[item_no]
            //is_root_post - относится ли подписка непосредственно к главному посту (если речь идёт о посте на одной странице) или же к комментарию-трекбечному посту
            this.form.innerHTML = formsCommon + 
                        '<div class="Friends-c-Subscribe" onclick="return {feed_id:\'' + 
                        this.commonParams.source.id + '\', item_no:\'' + this.commonParams.item_no + '\', subscription_status: \'' + 
                        _params[1].subscription_status + '\', is_root_post: ' + (this.commonParams.is_reply!==true) + '}" />';
        } else if (params.type == 'approve') {
            this.form.innerHTML = form_open+
                '<tr>'+
                    '<td></td>'+
                    '<th colspan="2">Принять эту запись</th>'+
                '</tr>'+
                '<tr>'+
                    '<td rowspan="2" class="userpics"></td>'+
                    '<td class="text">'+
                    '</td>'+
                    '<td class="b-actions-menu"></td>'+
                '</tr>'+
                '<tr>'+
                    '<td class="action">'+
                        '<div class="buttons">' +
                            this.getBtnsHTML('Принять') + formsCommon +
                        '</div>' +
                    '</td>'+
                    '<td></td>'+
                '</tr>'+
            form_close;
        } else if (params.type == 'ban_and_delete') {
            var action_source = g_sources.addSource(_params[1]);

            this.form.innerHTML = form_open+
                '<tr>'+
                    '<td></td>'+
                    '<th colspan="2">Забанить пользователя ' + action_source.yauser(true) + ' и удалить эту запись</th>'+
                '</tr>'+
                '<tr>'+
                    '<td rowspan="2" class="userpics"></td>'+
                    '<td class="text">'+
                        '<div class="b-quote-source">'+
                            '<input id="deleteAll" class="deleteAll" type="checkbox" name="delete_all"/>'+
                            '<label for="deleteAll"> Удалить все записи и комментарии пользователя в '+
                            ((action_source.type == 'community') ? 'этом клубе' : 'моём дневнике') + '</label>'+
                        '</div>'+
                    '</td>'+
                    '<td class="b-actions-menu"></td>'+
                '</tr>'+
                '<tr>'+
                    '<td class="action">'+
                        '<div class="buttons">' +
                            this.getBtnsHTML('Забанить и удалить') + formsCommon +
                            '<input type="hidden" name="author_uid" value="'+action_source.id+'"/>'
                        '</div>' +
                    '</td>'+
                    '<td></td>'+
                '</tr>'+
            form_close;
        } else if (params.type == 'wishlist') {
            var post = y5.$('post-' + params.post_id);
            var postContent = y5.Dom.getDescendant(post, 'div', 'b-wish');
            // параметры поста: title, url, img, market_id, market_url
            var wishlistParams = postContent.onclick();

            this.form.innerHTML = form_open +
                '<tr>' +
                    '<td></td>' +
                    '<th colspan="2">Захотеть то же самое</th>' +
                '</tr>' +
                '<tr>' +
                    '<td rowspan="2" class="userpics">' + userpics + '</td>' +
                    '<td class="text">' +
                        '<div class="b-write-priority"><div>как сильно хочу:</div>' +
                                '<span class="priority"><input type="radio" id="priority1" value="1" name="priority"/><label for="priority1">невыносимо</label></span> <span class="priority"><input type="radio" id="priority2" value="2" name="priority"/><label for="priority2">очень</label></span><span class="priority"><input type="radio" checked="checked" id="priority3" value="3" name="priority"/><label for="priority3">чтобы было</label></span></div>' +
                        '<p class="b-write-short-text">' +
                            '<textarea name="body" cols="40" rows="2" tabindex="1"></textarea>' +
                        '</p>' +
                        '<div class="keywordsContainer">' +
                        '</div>' +
                    '</td>' +
                    '<td class="b-actions-menu"></td>' +
                '</tr>' +
                '<tr>' +
                    '<td class="action">' +
                        this.getBtnsHTML() + formsCommon +
                        '<input type="hidden" name="title" value="' + wishlistParams.title + '"/>' +
                        '<input type="hidden" name="url" value="' + wishlistParams.url + '"/>' +
                        '<input type="hidden" name="image_from_link" value="' + wishlistParams.img + '"/>' +
                        '<input type="hidden" name="market_id" value="' + wishlistParams.market_id + '"/>' +
                        '<input type="hidden" name="market_url" value="' +wishlistParams. market_url + '"/>' +
                    '</td>'+
                    '<td></td>'+
                '</tr>'+
            form_close;

            var _this = this;

            y5.require(['Ajax', 'AjaxJS', 'Dom'], function() {
                function responce(request) {
                    var cont = y5.Dom.getDescendant(_this.form, 'div', 'keywordsContainer');
                    cont.innerHTML = request.html;
                    y5.Events.observe(
                            'click',
                            function(e) {
                                y5.Classes.assign(y5.Dom.getDescendant(_this.form, 'span', 'q-s-notification'), 'g-hidden', !e.target.checked);
                            },
                            y5.$('q-s'),
                            true,
                            _this)
                    y5.Components.init(_this.form);
                }
                var ajax = new y5.Ajax(
                    new y5.AjaxJS(g_source.id),
                    friendsURL('ajax/keywords'),
                    responce,
                    y5.Vars.NULL,
                    {}
                );
                ajax.send();
            });
        }

        y5.Classes.add(this.form, "b-quick-action b-quick-action-" + className);
        if (this.commonParams.transparent) {
            y5.Classes.add(this.form, "b-quick-action-notlogined");
        }
        this.form.action = formAction;

        var event_holder = Friends.ActionsControl;

        y5.Events.observe('y5:ActionsControl:FormIsReady', function() {
            this.onFormIsReady(_reply, _action, params);
        }, event_holder, true, this, true);

        y5.Components.init(this.form);

        if (params.type !== 'subscribe') {
            y5.Events.notify('y5:ActionsControl:FormIsReady', event_holder, true);
        }
    }

    this.onFormIsReady = function(_reply, _action, params) {
        //console.log('onFormIsReady');
        //коммент типа "подписаться на комменты" - пока -  не такой, как все
        var menu = this.menu;
        new Friends.SubmitShortcut(this.form);

        var dom_actions_menu = y5.Dom.getDescendant(this.form, 'td', 'b-actions-menu');
        if (dom_actions_menu) {
            dom_actions_menu.appendChild(menu);
        }

        insertAfter(this.form, _reply);
        this.textArea = getByTagName("textarea", this.form)[0];
        this.note = y5.Dom.getDescendant(this.form, 'b', 'b-note-attention') || document.createElement('b');

        // инициализируем Cancel
        var btnCancel = y5.Dom.getDescendants(this.form, 'input', 'submit')[1];
        new y5.AEventListener('click',
            function(e) {
                this.hideForm();
            },
            btnCancel, true, this
        );

        new y5.AEventListener('submit',
            function(e) {
                y5.Events.notify('y5:ActionsControl:syncTextModes', Friends.ActionsControl, true, {caller: this});
                // если форма не заполнена или ajax-запрос уже выполняется, то отправку отменяем
                if(!this.savingInProcess) {
                    this.submitForm(e, params);
                }
                e.preventDefault();
                e.stopPropagation();
            },
            this.form, true, this
        );

        init(this.form);

        // устанавливаем фокус
        y5.Form.activateFirstElement(this.form);
    }

    this.submitForm = function(e, params) {
            var _this = this;
            var formAction;
            // если была произведена смена портрета,
            // сохраняем id вновь выбранного userpic'а в g_source.currentUserpicId
            if (params.type == "userpic") {
                var selectedUserpicSpan = y5.Dom.getDescendant(_this.form, 'span', 'selected');
                if (selectedUserpicSpan) {
                    var selectedUserpicImg = selectedUserpicSpan.getElementsByTagName('img')[0];
                    g_source.currentUserpicId = selectedUserpicImg.id.replace('userpic-', '');
                }
            }

            // если комментарий добавил незалогиненный(!g_source)/незарегистрированный пользователь,
            // то сохраняем комментарий в сессию
            if (!g_source || (g_source.status != 'normal')) {
                formAction = friendsURL('ajax/replies_do_save_in_session');
            } else {
                formAction = friendsURL('ajax/replies_do_add');
            }

            // показываем прогрессбар
            // b-note-attention - класс блока для показа сообщения об ошибке
            // b-note-loading - класс блока для показа прогрессбара
            function startAjaxAnim() {
                y5.Classes.replace(_this.note, 'b-note-attention', 'b-note-loading');
                _this.note.innerHTML = '<i>&nbsp;</i>';
                _this.note.style.visibility = 'visible';
            }

            // скрываем прогрессбар
            function stopAjaxAnim() {
                _this.note.style.visibility = 'hidden';
                y5.Classes.replace(_this.note, 'b-note-loading', 'b-note-attention');
            }

            var savingReq = new y5.Request.Form(formAction, {method: 'post', transport: 'XML' });

            savingReq.onload = function(resp) {
                stopAjaxAnim();
                // IE почему-то не любит "\r\n" при преобразовании в json
                var response = resp.responseText.replace(/\r/g, ' ').replace(/\n/g, ' ');
                // экранируем бекслеш - cм. ECMA 262-3, 7.8.4
                response = response.replace(/(\\)(?!['"bfnrtv])/g,'$1$1');
                var respJSON = '';
                try {
                    respJSON = y5.JSON.decode(response);
                } catch (e) {
                    window.onerror("JSON decode error => ".concat(e.message), "replies_do_add.xml");
                    this.onerror();
                    return;
                }

                // в ответе ожидаем объект со свойстовм success
                if (respJSON.status && respJSON.status == 'Success') {
                    _this.savingInProcess = false;
                   if (params.type == 'join') {
                        y5.Events.notify('y5:joined', _this.control, true);
                    }
                    // если комментарий добавил незалогиненный(!g_source)/незарегистрированный(!g_source.qu) пользователь,
                    // то показываем форму логина/регистрации
                    if (!g_source || (g_source.status != 'normal')) {
                        var nextStep_form = y5.$('nextStepForm');
                        // удаляем форму комментария
                        y5.Dom.removeNode(_this.form);
                        // создаем обертку для формы, чтобы вставить после поста/комментария
                        // cloneNode использовать не можем, т.к. в IE возникает проблема из-за
                        // клонирования event handler'ов
                        var formContainer = y5.Elements.create('div');
                        formContainer.innerHTML = nextStep_form.innerHTML;
                        // вставляем форму логина/регистрации
                        _this.form = y5.Dom.insertAfter(formContainer, _this.getParent());

                        y5.Components.init(_this.form);
                    } else if (params.type == "subscribe") {
                        y5.Events.notify('y5:ActionsControl:onsubmit', _this.form, true, {success: true, caller: _this})
                    }
                    else if (!(params.type == 'approve' || params.type == 'ban_and_delete' || params.type == 'subsrcibe')) {
                        _this.hideForm();

                        var parentTr = y5.Dom.getAncestorOrSelf(_this.control, 'tr', '*');
                        var replies = y5.Dom.getDescendant(parentTr, 'code', 'b-replies-count');
                        if (replies && replies.control.switcher) {
                            var repliesControl = replies.control;
                            repliesControl.click(repliesControl, repliesControl.params);
                            if (repliesControl.isClosed()) {
                                repliesControl.click(repliesControl, repliesControl.params);
                            }
                            return;
                        }

                        // старый код -- на случай, если не выполнится код выше
                        var repliesControl = y5.$('cl-' + params.source.login + '-' + params.item_no);
                        var parent_id = '';
                        if (repliesControl.control.params[2]) {
                            parent_id = repliesControl.control.params[2];
                        }
                        repliesControl.control.loadReplies([params.source, params.item_no, parent_id, respJSON.id, repliesControl.control.params[4]], parent_id);
                    }
                    // для "принять" и "забанить и удалить" перегружаем страницу
                    // с заново сформированным меню "прокомментировать или" для "принять"
                    // и удаленным постом или постами и комментариями для "забанить и удалить"
                    else if (params.type == 'approve' || params.type == 'ban_and_delete') {
                        _this.hideForm();

                        window.location.href = window.location.href;
                    }
                } else {
                    window.onerror("Status = ".concat(respJSON.status), "replies_do_add.xml", -1, respJSON);
                    this.onerror(null, respJSON);
                }
            };
            savingReq.onerror = function(e, response) {
                // сбрасываем флаг "Сохранение в процессе"
                _this.savingInProcess = false;
                stopAjaxAnim();
                // показываем сообщение об ошибке
                if (params.type == 'subscribe') {
                    y5.Events.notify('y5:ActionsControl:onsubmit', _this.form, true, {success: false, caller: _this})
                } else {
                    var error_msg = "К сожалению, комментарий не удалось отправить. Попробуйте ещё раз.";
                    if (response && response.reason) {
                        error_msg = response.reason;
                    }
                    _this.note.innerHTML = error_msg;
                    _this.note.style.visibility = 'visible';
                }
            };
            savingReq.onexception = savingReq.onerror;

            // сохраняем комментарий (не созадавая дубли - проверяем флаг "Сохранение в процессе")
            if (!_this.savingInProcess) {
                savingReq.send(_this.form);
                _this.savingInProcess = true;
                startAjaxAnim();
            }

            return false;
    }

    this.preventSubmit = function(e, msg) {
        this.note.innerHTML = msg;
        this.note.style.visibility = 'visible';
        e.preventDefault();
    }

    this.init = function() {
        var _this = this;
        this.closeCallback = y5.Events.observe('y5:closeActionControl', this.hideForm, y5.Dom.getBody(), false, this);

        this.currentLink = y5.Dom.getDescendant(this.control, 'div', 'b-actions-list').getElementsByTagName("a")[0];
        this.addClickListener(this.currentLink);
        this.initLink = this.currentLink;
        this.button = y5.Dom.getDescendant(this.control, 'a', 'or');

        // открыть форму?
        var open = y5.Classes.test(this.control, "opened");

        if (open) {
            this.prepare();
            this.showHideForm(this.action);
        }

        if (!this.button) {
            return;
        }

        new y5.AEventListener('click',
            function(e) {
                if (!this.isPrepared) {
                    this.prepare();
                }

                this.showActions(e);
                e.stopPropagation();
                e.preventDefault();
            },
            this.button, true, this);

        /* Оптимизированное навешивание shortcut'а: создание при первом onfocus на "или", затем только add() */
        /* и remove() на onblur */
        // begin
        var shortCutEnter = null;

        new y5.AEventListener('focus',
            function(e) {
                if (!shortCutEnter) {
                    shortCutEnter = y5.ShortCut.down([{key: y5.ShortCut.ENTER}],
                        function(e) {
                            if (!_this.isPrepared) {
                                _this.prepare();
                            }

                            _this.showActions(e, true);
                            e.stopPropagation();
                            e.preventDefault();
                        },
                    this.button, false);
                } else {
                    shortCutEnter.add();
                }
            },
            this.button, true);

        new y5.AEventListener('blur',
            function(e) {
                if (shortCutEnter) {
                    shortCutEnter.remove();
                }
            },
            this.button, true);
        // end

        if (!g_source) {
            this.button.style.visibility = "hidden";
        }

        y5.Events.observe(
            'y5:Friends:show:wishToo', //actually we can use some other custom events here in future
            function() {
                this.click('wishlist');
            },
            this.control,
            true,
            this
        )
    };

    this.prepare = function() {
        var _this = this, actionType;
        this.action = 0;
        this.actions = [];
        this.actionsTypes = {};
        if (y5.is_ie) {
            this.repliesContainer = y5.Dom.getAncestorOrSelf(this.control, 'div', 'b-page-post-replies');
        }

        if (this.button) {
            this.initActionsMenu();

            // debugger;
            var holster = y5.Dom.getDescendant(this.actionsMenu, 'div', 'holster');
            // получаем массив ссылок-действий, исключая ссылку "или"
            this.actions = y5.Dom.getDescendants(holster, 'a', /(?!:or)/);
            for (var i = 0, l = this.actions.length; i < l; i++) {
                var action = this.actions[i];
                actionType = getParams(action);
                // если в onclick не задан массив параметров (например, для "комментировать оригинал")
                if (actionType && y5.Types.array(actionType)) {
                    this.prepareAction(i, action, actionType);
                    this.addClickListener(action);
                    createClickListener(i, action);
                    // в commonParams.type прописан тип комментария, который приходит в парамтере урла for_reply
                    // если он задан, то на странице одного поста replies.xml надо открывать форму комментария заданного типа
                    // деалем это, задавая this.action
                    if (this.commonParams && this.params.length && this.params[i][0] == this.commonParams.type) {
                        this.action = i;
                    }
                }
            }
        } else {
            // если действие только одно, то меню справа от формы не создается,
            // и обработчики на click навешивать не надо
            // (случай комментария для незарегистрированного/незалогиненного пользователя)
            actionType = getParams(this.currentLink);
            this.prepareAction(0, this.currentLink, actionType);
        }

        this.isPrepared = true;

        function createClickListener(index, link) {
            new y5.AEventListener('click',
                function(e) {
                    _this.adjustActionsMenu(false);
                    _this.action = index;
                },
            link, true);
        }
    };

    this.prepareAction = function(actionNumber, actionLink, actionType) {
        this.params[actionNumber] = actionType;
        this.actionsTypes[[actionType[0]]] = actionNumber;
    };

    this.addClickListener = function(actionLink) {
        new y5.AEventListener('click', function(e) {
            var actionType = getParams(e.target)
            // если в onclick не задан массив параметров (например, для "комментировать оригинал"),
            // значит, форму комментария создавать не надо (просто прямой переход по ссылке)
            if (!(actionType && y5.Types.array(actionType))) {
                return;
            }
            this.click(actionType);
            e.preventDefault();
            e.stopPropagation();
        }, actionLink, true, this);
    };

    this.destruct = function()
    {
        this.action = null;

        for (var i = 0, l = this.actions.length; i < l; i++)
        {
            this.actions[i].onclick = null;
            this.actions[i] = null;
            this.params[i] = null;
        }

        this.params = null;
        this.actions = null;

        if (this.button) {
            //if (this.button.onclick) {
            //    this.button.onclick = null;
            //}
            this.button = null;
        }

        this.commonParams = null;
        this.control = null;
    };

    this.initActionsMenu = function() {
        this.actionsMenu = y5.Dom.getDescendant(this.control, 'div', 'b-actions-select');
        this.actionsMenusContainer.appendChild(this.actionsMenu);

        var links = this.actionsMenu.getElementsByTagName('a');
        this.actionsMenuFirstLink = links[0];
        // в родительском узле первой ссылки-действия размещена ссылка "или"
        this.actionsMenu_button = links[1]; //y5.Dom.getDescendant(this.actionsMenuFirstLink.parentNode, 'a', 'or');
        this.actionsList = y5.Dom.getDescendant(this.control, 'div', 'b-actions-list');

        y5.Utils.fakeFrame.init(this.actionsMenu);

        var _this = this;

        // out listener
        if (y5.Vars.is_ie) {
            this.actionsMenu.outListener = new y5.AEventListener('focusout',
                function(e) {
                    if (y5.Dom.isChild(e.toElement, _this.actionsMenu)) {
                        return;
                    }
                    _this.adjustActionsMenu(false);
                },
            this.actionsMenu, false);
        } else if (y5.Vars.is_opera) {
            this.actionsMenu.outListener = new y5.AEventListener('keyup',
                function(e) {
                    if (y5.Dom.isChild(e.target, _this.actionsMenu)) return;
                    _this.adjustActionsMenu(false);
                },
            document, false);
        }  else {
            this.actionsMenu.outListener = new y5.AEventListener('blur',
                function(e) {
                    if (y5.Dom.isChild(e.explicitOriginalTarget, _this.actionsMenu)) {
                        return;
                    }
                    _this.adjustActionsMenu(false);
                },
            document, false);
        }

        this.actionsMenu.mousedownListener = new y5.AEventListener('mousedown',
            function(e) {
                if (y5.Dom.isChild(e.target, _this.actionsMenu)) return;
                _this.adjustActionsMenu(false);
            },
        document, false);
        this.actionsMenu.resizeListener = new y5.AEventListener('resize',
            function(e) {
                _this.adjustActionsMenu(false);
            },
        window, false);
        new y5.AEventListener('click',
            function(e) {
                _this.adjustActionsMenu(false);
                e.preventDefault();
                e.stopPropagation();
            },
        this.actionsMenu_button, true);
    };

    this.adjustActionsMenu = function(show, withDefaultFocus) {
        if (show == null) {
            show = (0 == (this.actionsMenu.offsetHeight || 0));
        }
        if (show) {
            var position = y5.Dom.getOffsset(this.actionsList, true);
            this.actionsMenu.style.top = position[1] - 6 + 'px';
            this.actionsMenu.style.left = position[0] - 3 + 'px';
            this.actionsMenu.style.display = 'block';
            if (withDefaultFocus) {
                if ((this.actionsMenuFirstLink.offsetHeight || 0) > 0) {
                    this.actionsMenuFirstLink.focus();
                }
            } else if (this.actionsMenu.focus) {
                this.actionsMenu.focus();
                this.actionsMenu.blur();
            }

            this.actionsMenu.outListener.add();
            this.actionsMenu.mousedownListener.add();
            this.actionsMenu.resizeListener.add();
        } else {
            this.actionsMenu.style.display = 'none';

            this.actionsMenu.outListener.remove();
            this.actionsMenu.mousedownListener.remove();
            this.actionsMenu.resizeListener.remove();
        }
        y5.Utils.fakeFrame.adjust(this.actionsMenu);
    };
}

y5.require(['Strings', 'Dom', 'Arrays', 'Classes', 'ShortCuts', 'cssQuery', 'Events', 'Utils', 'Form', 'Template', 'URL', 'Widget', 'Ajax', 'AjaxForm', 'Request', '{Friends}.Subscribe', '{Friends}.SubmitShortcut'],
    function() {
        y5.loaded('{Friends}.ActionsControl');
    }
);
