Greasy Fork is available in English.

ComfortablePlayingInGW

Веселые плюшки для Gwars

// ==UserScript==
// @name            ComfortablePlayingInGW
// @namespace       https://github.com/MyRequiem/comfortablePlayingInGW
// @description     Веселые плюшки для Gwars
// @id              comfortablePlayingInGW@MyRequiem
// @include         https://*gwars*
// @include         https://*ganjafoto*
// @include         http*://*ganjafile*
// @grant           none
// @license         MIT
// @version         1.176-200424
// @author          MyRequiem [https://www.gwars.io/info.php?id=2095458]
// ==/UserScript==

//     ______                ____           __        __    __
//    / ____/___  ____ ___  / __/___  _____/ /_____ _/ /_  / /__
//   / /   / __ \/ __ `__ \/ /_/ __ \/ ___/ __/ __ `/ __ \/ / _ \
//  / /___/ /_/ / / / / / / __/ /_/ / /  / /_/ /_/ / /_/ / /  __/
//  \____/\____/_/ /_/ /_/_/  \____/_/   \__/\__,_/_.___/_/\___/
//
//      ____  __            _                ____         _______       __
//     / __ \/ /___ ___  __(_)___  ____ _   /  _/___     / ____/ |     / /
//    / /_/ / / __ `/ / / / / __ \/ __ `/   / // __ \   / / __ | | /| / /
//   / ____/ / /_/ / /_/ / / / / / /_/ /  _/ // / / /  / /_/ / | |/ |/ /
//  /_/   /_/\__,_/\__, /_/_/ /_/\__, /  /___/_/ /_/   \____/  |__/|__/
//                /____/        /____/

// lint {{{1
/*global unsafeWindow */
/*jslint browser: true, maxlen: 80, regexp: true, vars: true, plusplus: true,
    continue: true, devel: true, nomen: true
*/

/*eslint-env browser */
/*eslint no-useless-escape: 'warn', linebreak-style: ['error', 'unix'],
    quotes: ['error', 'single'], semi: ['error', 'always'],
    eqeqeq: 'error', curly: 'error'
*/

/*jscs:disable requireMultipleVarDecl, requireVarDeclFirst */
/*jscs:disable disallowKeywords, disallowDanglingUnderscores */
/*jscs:disable validateIndentation */
// 1}}}

(function () {
    'use strict';

    /**
     * @class General {{{1
     * @constructor
     */
    var General = function () {
        /**
         * @property root
         * @type {Object}
         */
        this.root = this.getRoot();
        /**
         * @property doc
         * @type {Object}
         */
        this.doc = this.root.document;
        /**
         * @property loc
         * @type {String}
         */
        this.loc = this.root.location.href;
        /**
         * @property st
         * @type {Object}
         */
        this.st = this.root.localStorage;
        /**
         * @property cons
         * @type {Object}
         */
        this.cons = this.root.console;
        /**
         * @property version
         * @type {String}
         */
        this.version = '1.176-200424';
        /**
         * @property stString {{{2
         * @type {String}
         */
        this.stString = this.version +  // [0] - script version
                    /*
                    [1]  - initScript
                        [0]  - NotGiveCannabisLeaf
                        [1]  - AdditionForNavigationBar
                        [2]  - AdsFilter (удален)
                        [3]  - AdvBattleAll
                        [4]  - BlacklistHighlighting
                        [5]  - WorkPostGrenadesBroken
                        [6]  - ResourcesAndBonuses
                        [7]  - CritShotsAndLinksBtlLog
                        [8]  - DeleteSms
                        [9]  - FarmExperience
                        [10] - FarmTimer
                        [11] - ComfortableLinksForFarm (удален)
                        [12] - TimeNpc
                        [13] - AllPlantsOnFarm
                        [14] - GwMenu
                        [15] - InventoryPlus
                        [16] - CountBattles
                        [17] - GbCounter
                        [18] - BonusInfo (удален)
                        [19] - BuyHightech
                        [20] - NewsAndInvit
                        [21] - DoFilter (удален)
                        [22] - FilterResOnStat
                        [23] - FilterWarlistOne2One
                        [24] - FixSkills
                        [25] - FuckTheFarm
                        [26] - HistorySms
                        [27] - LinksToHighTech
                        [28] - GameMania
                        [29] - GosEnergoAtomFilter
                        [30] - SortSyndOnline
                        [31] - HousHealth
                        [32] - SortSyndWars (удален)
                        [33] - LinksInOne2One
                        [34] - One2OneCallerInfo
                        [35] - PersonalNPCNotifications
                        [36] - PortTimer
                        [37] - PortsAndTerminals
                        [38] - RangeWeapon
                        [39] - RentAndSale
                        [40] - ScanKarma
                        [41] - ScanPers
                        [42] - ShowInitMessOnForum
                        [43] - SearchUser
                        [44] - SkillCounters
                        [45] - SyndPtsAnalyser
                        [46] - SyndAnalyser
                        [47] - ShowMyAchievements
                        [48] - SyndPersInfo (удален)
                        [49] - SyndOnlineOnMainPage
                        [50] - TimeKarma
                        [51] - ImgPokemonsOnBattle
                        [52] - SoundSyndBattle
                        [53] - AdvForum
                        [54] - DelAndAddBlackSms
                        [55] - FilterGeneralFighting (удален)
                        [56] - Regeneration
                        [57] - ProfColor
                        [58] - CurrentQuestOnInfo
                        [59] - CommonBattleFilter
                        [60] - CalculateSyndLvl (удален)
                        [61] - PortsSyndLinks
                        [62] - BbCodeInMessages
                        [63] - ProfessionLevels */
                        '@||||||||||' +
                        '||||||||||' +
                        '||||||||||' +
                        '||||||||||' +
                        '||||||||||' +
                        '||||||||||' +
                        '|||' +
                    /*
                    [2]  - AdditionForNavigationBar
                        [0] - '{"linkName": ["href", "style"], ...}' */
                        '@' +
                    /*
                    [3]  - AdsFilter (удален)
                        [0] - остров (нет,Z,G: '', 1, 2)
                        [1] - фильтр по онлайну */
                        '@|' +
                    /*
                    [4]  - AdvBattleAll
                        # настройки
                        [0]  - таймаут обновления данных в бою
                        [1]  - таймаут обновления страницы, когда висим в заявке
                        # основные данные скрипта
                        [2]  - метод сортировки списка врагов (0 - 5)
                        [3]  - чекбокс "запомнить ход"
                        [4]  - чекбокс "не дублировать цель" (для двуруких)
                        # последний сделаный ход (если включено "запомнить ход")
                        [5]  - левая
                        [6]  - правая
                        [7]  - куда отходим
                        [8]  - кидаем грену или нет
                        [9]  - подходим или нет
                        [10] - чекбокс <Сказать своей команде>
                        # запоминаем ход в хранилище перед тем как сказать ход
                        [11] - номер в кого стреляем
                        [12] - направление левой руки
                        [13] - направление правой руки
                        [14] - куда отходим
                        [15] - кидаем грену или нет
                        [16] - подходим или нет
                        [17] - список выбора врагов (хэш: имя --> номер)
                        [18] - чекбокс "Говорить только левую руку"
                        [19] - общий навык
                        [20] - навык специалиста
                        [21] - звук при начале боя
                        [22] - звук при начале хода
                        [23] - чекбокс "Говорить только правую руку"
                        [24] - подствольник
                        [25] - показывать чекбокс "Сказать как координатор"
                        [26] - чекбокс "Сказать как координатор" отмечен
                        ### при нажатии на кнопку "Сказать ход":
                        [27] - не выводить имя противника
                        [28] - не выводить применяемые навыки */
                        '@||||||||||||||||||||||||||||' +
                    /*
                    [5]  - BlacklistHighlighting
                        [0]  - ID персов из ЧС ('id1,id2,...')
                        [1]  - блокировать ссылку принятия боя в одиночках? */
                        '@|' +
                    /*
                    [6]  - WorkPostGrenadesBroken
                        [0]  - звук при получении почты/посылки (проигран, нет)
                        [1]  - звук по окончании работы (проигран или нет)
                        [2]  - отображать время работы
                        [3]  - отображать почту/посылку
                        [4]  - отображать слом
                        [5]  - отображать грену
                        [6]  - звук при получении почты/посылки
                        [7]  - звук "Пора работать" */
                        '@|||||||' +
                    /*
                    [7]  - CritShotsAndLinksBtlLog
                        [0]  - показывать/не показывать критические выстрелы */
                        '@' +
                    /*
                    [8]  - DeleteSms
                        [0]  - отмечать синдовые рассылки
                        [1]  - отмечать рассылки от робота
                        [2]  - НЕ отмечать письма с пометкой "важное" */
                        '@||' +
                    /*
                    [9]  - FarmTimer
                        [0]  - время полива/сбора
                        [1]  - действие (Полить|Собрать)
                        [2]  - время последнего проигрывания звука
                        [3]  - звук проигран?
                        [4]  - номер звука когда пора поливать/собирать
                        [5]  - интервал повторения звука */
                        '@|||||' +
                    /*
                    [10] - TimeNpc
                        [0]  - звук вкл/выкл
                        [1]  - ID NPC, у которого последний раз брали квест
                        [2]  - время
                        [3]  - номер звука */
                        '@|||' +
                    /*
                    [11] - AllPlantsOnFarm
                        [0]  - номер первого недоступного растения
                                (не используется с версии 1.82)
                        [1]  - время сброса счетчика
                        [2]  - количество гб
                        [3]  - количество производа
                        [4]  - показывать счетчик ГБ?
                        [5]  - показывать счетчик производа? */
                        '@|||||' +
                    /*
                    [12] - GwMenu
                        [0] - чекбокс "Показывать всегда" */
                        '@' +
                    /*
                    [13] - GbCounter
                        [0] - количество Гб */
                        '@' +
                    /*
                    [14] - NewsAndInvit
                        [0] - {'newsId': 0|1, ...} */
                        '@' +
                    /*
                    [15] - FilterResOnStat
                        [0] - список отображаемых ресурсов через запятую */
                        '@' +
                    /*
                     [16] - FilterWarlistOne2One
                        [0] - название оружия */
                        '@' +
                    /*
                     [17] - GosEnergoAtomFilter
                        [0] - остров ('' - любой, 'Z', 'G')
                        [1] - тип объекта ('' - любой, '1' - эски, '2' - уранки,
                                '3' - уранки [2], '4' - уранки [3])
                        [2] - синдикат ('', '0' - ничейки, 'xxx' - ID синда) */
                        '@' +
                    /*
                     [18] - PersonalNPCNotifications
                        [0] - звук,когда NPC ожидает распоряжений
                        [1] - звук, когда NPC находится на Аутленде и его
                                здоровье менее 30%
                        [2] - null (осталось от SortSyndOnline) */
                        '@||' +
                    /*
                     [19] - HousHealth
                        [0] - вышел из боя */
                        '@' +
                    /*
                     [20] - CommonBattleFilter
                        [0] - максимальный уровень
                        [1] - чекбокс "без именных"
                        [2] - чекбокс "по  мощности" */
                        '@0|1|1' +
                    /*
                     [21] - One2OneCallerInfo
                        [0] - звук при вызове */
                        '@' +
                    /*
                     [22] - PortTimer
                        [0] - '{
                            date: "",
                            syndid: "",
                            time: [],
                            current: ""}' */
                        '@' +
                    /*
                     [23] - ScanKarma
                        [0] - текущая карма 'xx/xx' */
                        '@' +
                    /*
                     [24] - ScanPers
                        [0] - id персонажа
                        [1] - id синда персонажа
                        [2] - чекбокс звук
                        [3] - чекбокс сообщение
                        [4] - id звука при входе
                        [5] - id звука при выходе
                        [6] - звук(сообщение) проигран или нет
                        [7] - ник персонажа
                        [8] - timestamp последнего сканирования */
                        '@||||||||' +
                    /*
                     [25] - SkillCounters
                        [0] - боевой
                        [1] - эконом
                        [2] - производ
                        [3] - пистолеты
                        [4] - гранаты
                        [5] - автоматы
                        [6] - пулеметы
                        [7] - дробовики
                        [8] - снайперки
                        [9] - синдикатный уровень
                        [10] - время последнего сброса */
                        '@||||||||||' +
                    /*
                     [26] - ShowMyAchievements
                        [0] - номера ачивок '1,14,35...' */
                        '@' +
                    /*
                     [27] - TimeKarma
                        [0] - время, когда выставили карму */
                        '@' +
                    /*
                     [28] - SoundSyndBattle
                        [0] - время до боя, когда будет проигран второй звук
                        [1] - звук №1
                        [2] - звук №2
                        [3] - флаги проигрывани звука ('', '1', '2') */
                        '@|||' +
                    /*
                     [29] - AdvForum
                        [0] - отмечать закрытые темы
                        [1] - звук при появлении новой темы
                        [2] - интервал перезагрузки страниц с темами форума
                        [3] - не показывать закрытые темы
                        [4] - не показывать прикрепленки
                        [5] - ветки, где скрипт работать не будет
                        [6] - список включения/отключения показа форумов
                        [7] - данные веток форума:
                          {
                              'fid': {
                                  'tid': {
                                      d: str  // дата первого просмотра темы
                                      l: str  // id последнего сообщения
                                      с: str  // номер последнего сообщения
                                  },
                                  ...
                              },
                              ...
                          }
                        [8] - {'fid': id_последней темы, ...} */
                        '@1|||||1,5,7,11,15,24,34,35,36,37,41,42,44,46,47,' +
                        '48,54|1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,' +
                        '1|{}|{}' +
                    /*
                     [30] - FilterGeneralFighting (удален)
                        [0]  - минимальный уровень
                        [1]  - максимальный уровень
                        [2]  - командные
                        [3]  - случайные
                        [4]  - DM
                        [5]  - без DM
                        [6]  - без синдовых
                        [7]  - только синдовые
                        [8]  - показывать умения
                        [9]  - показывать дальность
                        [10] - нужная дальность
                        [11] - время до боя (сек) */
                        '@5|50||||||||||' +
                    /*
                    [31] - Regeneration
                        [0] - номер звука при 80%
                        [1] - номер звука при 100%
                        [2] - текущее состояние:
                                '' : пустое
                                '1': получили 80%
                                '2': получили 100%
                                '3': получили 80%, но еще не получили 100%
                                '4': финиш
                    */
                        '@||'; // 2}}}
        /**
         * @property myID
         * @type {RegExpExecArray}
         */
        this.myID = /(^|;) ?uid=([^;]*)(;|$)/.exec(this.doc.cookie);
        /**
         * @property DESIGN_VERSION
         * @type {RegExpExecArray}
         */
        this.DESIGN_VERSION = /(^|;) ?version=([^;]*)(;|$)/.
                exec(this.doc.cookie);
        /**
         * @property selfName
         * @type {String}
         */
        this.selfName = 'comfortablePlayingInGW';
        /**
         * @property STORAGENAME
         * @type {String}
         */
        this.STORAGENAME = (this.myID ? this.myID[2] : '') + '_' +
            this.selfName;
        /**
         * @property mainPath
         * @type {String}
         */
        this.mainPath = 'https://raw.githubusercontent.com/MyRequiem/' +
            this.selfName + '/master/';
        /**
         * @property imgPath
         * @type {String}
         */
        this.imgPath = this.myID && this.myID[2] === '2095458' ?
                'http://127.0.0.1/imgs/gwScripts/' : this.mainPath + 'imgs/';
        /**
         * @property viewMode
         * @type {Boolean}
         */
        this.viewMode = /\/(warlog|battlelog)\.php/.test(this.loc);
        /**
         * @property domain
         * @type {String}
         */
        this.domain = this.doc.domain;
        /**
         * @property mainDomain
         * @type {Boolean}
         */
        this.mainDomain = /gwars/.test(this.loc);
    }; // 1}}}

    /**
     * @lends General.prototype {{{1
     */
    General.prototype = {
        /**
         * @method getRoot {{{2
         * @return  {Object}
         */
        getRoot: function () {
            var rt = typeof unsafeWindow;
            return rt !== 'undefined' ? unsafeWindow : window;
        }, // 2}}}

        /**
         * @method setNewStorage {{{2
         */
        setNewStorage: function () {
            var oldSt = this.st.getItem(this.STORAGENAME);
            if (!oldSt) {
                this.st.setItem(this.STORAGENAME, this.stString);
                return;
            }

            // если были старые данные, переносим их в новую storage-строку
            var newSt = this.stString.split('@');
            oldSt = oldSt.split('@');
            var nSt, oSt, i, j;
            for (i = 1; i < oldSt.length; i++) {
                nSt = newSt[i].split('|');
                if (nSt.length === 1) {
                    newSt[i] = oldSt[i];
                    continue;
                }

                oSt = oldSt[i].split('|');
                for (j = 0; j < oSt.length; j++) {
                    nSt[j] = oSt[j];
                }

                newSt[i] = nSt.join('|');
            }

            this.st.setItem(this.STORAGENAME, newSt.join('@'));
        }, // 2}}}

        /**
         * @method getData {{{2
         * @param   {int}   ind
         * @return  {Array}
         */
        getData: function (ind) {
            return this.st.getItem(this.STORAGENAME).
                split('@')[ind].split('|');
        }, // 2}}}

        /**
         * @method setData {{{2
         * @param   {String|Array}  data
         * @param   {int}           ind
         */
        setData: function (data, ind) {
            var dt = this.st.getItem(this.STORAGENAME).split('@');
            dt[ind] = typeof data !== 'string' ? data.join('|') : data;
            this.st.setItem(this.STORAGENAME, dt.join('@'));
        }, // 2}}}

        /**
         * @method getInitScript {{{2
         * @return  {Array}
         */
        getInitScript: function () {
            if (this.mainDomain) {
                return this.getData(1);
            }

            return [];
        }, // 2}}}

        /**
         * @method $ {{{2
         * @param   {String}    id
         * @return  {HTMLElement|null}
         */
        $: function (id) {
            return this.doc.querySelector('#' + id);
        }, // 2}}}

        /**
         * @method checkMainData {{{2
         * @return  {Boolean}
         */
        checkMainData: function () {
            if (!this.st) {
                alert('Ваш браузер не поддерживает технологию localStorage');
                return false;
            }

            if (this.mainDomain) {
                if (!this.myID || !this.DESIGN_VERSION) {
                    this.cons.log('!this.myID || !this.DESIGN_VERSION');
                    return false;
                }

                if (!this.st.getItem(this.STORAGENAME) ||
                        this.getData(0)[0] !== this.version) {
                    this.setNewStorage();
                }

                this.myID = this.myID[2];
                this.DESIGN_VERSION = this.DESIGN_VERSION[2];
            }

            return true;
        } // 2}}}
    }; // 1}}}

    var general, initScript;

    /**
     * @class GetStrDate {{{1
     * @constructor
     */
    var GetStrDate = function () {
        /**
         * @method init
         * @param   {int|String}    time
         * @return  {String}
         */
        this.init = function (time) {
            var date = time === 'now' ? new Date() : new Date(time),
                month = date.getMonth() + 1,
                day = date.getDate();

            return (day < 10 ? '0' + day : day) +  '.' +
                        (month < 10 ? '0' + month : month) + '.' +
                            /20(\d+)/.exec(date.getFullYear().toString())[1];
        };
    }; // 1}}}

    /**
     * @class GetTimestamp {{{1
     * @constructor
     */
    var GetTimestamp = function () {
        /**
         * @method init
         * @param   {String}    val
         * @return  {int}
         */
        this.init = function (val) {
            var date = /(\d\d)\.(\d\d)\.(\d\d)/.exec(val);

            if (!date) {
                return 0;
            }

            var d = +date[1],
                m = +date[2],
                y = +date[3];

            if (!d || d > 31 || !m || m > 12 || !y) {
                return 0;
            }

            return new Date(2000 + y, m - 1, d).getTime();
        };
    }; // 1}}}

    /**
     * @class UrlEncode {{{1
     * @constructor
     */
    var UrlEncode = function () {
        /**
         * @method init
         * @param   {String}    str
         * @return  {String}
         */
        this.init = function (str) {
            var mass = {1040: 192, 1041: 193, 1042: 194, 1043: 195, 1044: 196,
                    1045: 197, 1046: 198, 1047: 199, 1048: 200, 1049: 201,
                    1050: 202, 1051: 203, 1052: 204, 1053: 205, 1054: 206,
                    1055: 207, 1056: 208, 1057: 209, 1058: 210, 1059: 211,
                    1060: 212, 1061: 213, 1062: 214, 1063: 215, 1064: 216,
                    1065: 217, 1066: 218, 1067: 219, 1068: 220, 1069: 221,
                    1070: 222, 1071: 223, 1072: 224, 1073: 225, 1074: 226,
                    1075: 227, 1076: 228, 1077: 229, 1078: 230, 1079: 231,
                    1080: 232, 1081: 233, 1082: 234, 1083: 235, 1084: 236,
                    1085: 237, 1086: 238, 1087: 239, 1088: 240, 1089: 241,
                    1090: 242, 1091: 243, 1092: 244, 1093: 245, 1094: 246,
                    1095: 247, 1096: 248, 1097: 249, 1098: 250, 1099: 251,
                    1100: 252, 1101: 253, 1102: 254, 1103: 255, 1025: 168,
                    1105: 184, 8470: 185},
                result = '',
                code,
                i;

            for (i = 0; i < str.length; i++) {
                code = str.charCodeAt(i);
                code = mass[code] || code;

                if (code < 16) {
                    result += '%0' + code.toString(16);
                } else {
                    result += '%' + code.toString(16);
                }
            }

            return result;
        };
    }; // 1}}}

    /**
     * @class PlaySound {{{1
     * @constructor
     */
    var PlaySound = function () {
        /**
         * @method init
         * @param   {int|String}    sound
         */
        this.init = function (sound) {
            if (sound && sound !== '0') {
                var audio = general.$('cpingw_audio');
                if (!audio) {
                    audio = general.doc.createElement('audio');
                    audio.setAttribute('id', 'cpingw_audio');
                    var divAudio = general.doc.createElement('div');
                    divAudio.setAttribute('style', 'display: none;');
                    divAudio.appendChild(audio);
                    general.doc.body.appendChild(divAudio);
                }

                audio.volume = 0.2;
                audio.src = (general.myID === '2095458' ?
                        'http://127.0.0.1/' : general.mainPath) + 'sounds/' +
                            sound + '.ogg';
                // noinspection JSIgnoredPromiseFromCall
                audio.play();
            }
        };
    }; // 1}}}

    /**
     * @class GetRandom {{{1
     * @constructor
     */
    var GetRandom = function () {
        /**
         * @method init
         * @param   {int}   a
         * @param   {int}   b
         * @return  {int}
         */
        this.init = function (a, b) {
            return Math.round(a + (b - a) * Math.random());
        };
    }; // 1}}}

    /**
     * @class GetTopPanel {{{1
     * @constructor
     */
    var GetTopPanel = function () {
        /**
         * @method init
         * @return  {HTMLElement|null}
         */
        this.init = function () {
            // ищем верхнюю панель "MyRequiem [603/603] ... 21:01, 3095 онлайн"
            var topPanel;

            if (general.DESIGN_VERSION === 'v2') {  // новый дизайн
                topPanel = general.doc.querySelector('td.gw-header-col2 ' +
                        'td[width="50%"][valign="middle"]');
                if (topPanel) {
                    topPanel.setAttribute('style', 'width: 70%;');
                }
            } else {
                topPanel = general.doc.
                    querySelector('td.txt[align="left"] nobr:first-child');
                if (topPanel) {
                    // noinspection JSUnresolvedFunction
                    topPanel.parentNode.setAttribute('style', 'width: 70%;');
                }
            }

            return topPanel;
        };
    }; // 1}}}

    /**
     * @class SetSettingsButton {{{1
     * @constructor
     */
    var SetSettingsButton = function () {
        /**
         * @method init
         */
        this.init = function () {
            var topPanel = new GetTopPanel().init();

            if (topPanel) {
                // noinspection JSUnresolvedVariable
                var settingsButton = general.doc.createElement('a'),
                    target = topPanel.nextElementSibling ||
                        topPanel.parentNode.nextElementSibling;

                settingsButton.innerHTML = '<img src="' + general.imgPath +
                    'NotGiveCannabisLeaf/on.gif" width="15" height="15" ' +
                    'title="Настройки" alt="Настройки" />';
                settingsButton.setAttribute('href',
                        'https://' + general.domain + '/news.php?set=1');
                settingsButton.setAttribute('style', 'margin-left: 7px;');
                target.querySelector('table td:last-child>nobr').
                    appendChild(settingsButton);
            }
        };
    }; // 1}}}

    /**
     * @class AjaxQuery {{{1
     * @constructor
     */
    var AjaxQuery = function () {
        /**
         * @method init
         * @param   {String}        url
         * @param   {String}        rmethod
         * @param   {String|null}   param
         * @param   {Boolean}       async
         * @param   {Function}      onsuccess
         * @param   {Function|null} onfailure
         */
        this.init = function (url, rmethod, param, async, onsuccess,
                onfailure) {
            var xmlHttpRequest = new XMLHttpRequest();

            if (!xmlHttpRequest) {
                general.cons.log('Error create xmlHttpRequest !!!');
                return;
            }

            xmlHttpRequest.open(rmethod, url, async);

            if (rmethod === 'POST') {
                xmlHttpRequest.setRequestHeader('Content-Type',
                    'application/x-www-form-urlencoded');
                xmlHttpRequest.setRequestHeader('Content-length',
                    param.length.toString());
                xmlHttpRequest.setRequestHeader('Connection', 'close');
            }

            xmlHttpRequest.send(param);

            if (async) {
                var timeout = general.root.setTimeout(function () {
                    xmlHttpRequest.abort();
                }, 10000);

                xmlHttpRequest.onreadystatechange = function () {
                    if (xmlHttpRequest.readyState === 4) {
                        clearTimeout(timeout);
                        if (xmlHttpRequest.status === 200) {
                            onsuccess(xmlHttpRequest);
                        } else if (onfailure) {
                            onfailure();
                        }
                    }
                };
            } else {
                if (xmlHttpRequest.status === 200) {
                    onsuccess(xmlHttpRequest);
                } else {
                    onfailure(xmlHttpRequest);
                }
            }
        };
    }; // 1}}}

    /**
     * @class CheckInputText {{{1
     * @constructor
     */
    var CheckInputText = function () {
        /**
         * @method init
         * @param   {Object}    inp
         * @param   {int}       limit
         * @return  {Boolean}
         */
        this.init = function (inp, limit) {
            var _inp = inp,
                val = _inp.value,
                lim = limit || 0;

            return !(!val || isNaN(+val) || +val < lim);
        };
    }; // 1}}}

    /**
     * @class GetSelectSound {{{1
     * @constructor
     */
    var GetSelectSound = function () {
        /**
         * @method init
         * @param   {String}    id
         * @return  {String}
         */
        this.init = function (id) {
            var sounds = [
                    'Без звука', 'Перезарядка', 'Выстрел дробовика',
                    'Открытие двери', 'Взрыв бочки', 'Выстрел BFG',
                    'Радио-зуммер', 'Подтверждение цели', 'Ion Cannon Ready!',
                    'Select target!', 'Звук тревоги', 'I`m alive!',
                    'Орки смеются', 'Unholy Armor', 'We`ve been attacked!',
                    'Кот мяукает', 'Кот мяукает #2', 'Take cover!', 'Stupid!',
                    'Hello!', 'hehehehe!', 'Chimes', 'Ding', 'Ошибка',
                    'Отказ оборудования', 'А, вот эти ребята', 'Не-не-не-не!',
                    'нет, Девид Блейн, нет!',
                    'Я делаю особую магию&nbsp;&nbsp;', 'Prepare for battle!',
                    'Pick up your weapons'
                ],
                str = '<select id="' + id + '" disabled>',
                i;

            for (i = 0; i < sounds.length; i++) {
                str += '<option value="' + i + '">' + sounds[i] + '</option>';
            }

            return str + '</select> ' +
                '<input type="button" id="l' + id + '" ' +
                'value="»" disabled>';
        };
    }; // 1}}}

    /**
     * @class ShowMainSettings {{{1
     * @constructor
     */
    var ShowMainSettings = function () {
        /**
         * @property getSelectSound
         * @type {Function}
         */
        this.getSelectSound = new GetSelectSound().init;
        // noinspection HtmlUnknownAttribute
        /**
         * @property infoScripts {{{2
         * @type {Object}
         */
        this.infoScripts = {
            'Персонаж': [
                ['Gw-Меню', 'Панель с множеством полезных ссылок для игры.' +
                    this.getGitHubLink('gwMenu'), '14'],
                ['Логотип игры', 'На всех страницах заменяет логотип игры ' +
                    '&nbsp;&nbsp;<img style="box-shadow: 2px 3px 3px ' +
                    'rgba(122,122,122, 0.5);" src="https://images.' +
                    general.domain.replace('www.', '') +
                    '/i/gon.gif" alt="img" /> &nbsp;&nbsp;на зеленый ' +
                    'листик &nbsp;&nbsp;<img style="box-shadow: 2px 3px 3px ' +
                    'rgba(122,122,122,0.5);" src="' + general.imgPath +
                    'NotGiveCannabisLeaf/on.gif" alt="img" />' +
                    this.getGitHubLink('notGiveCannabisLeaf'), '0'],
                ['Таймер выздоровления', 'Таймер выздоровления персонажа ' +
                    'на главной странице.<br>' +
                    this.getSelectSound('sound80HP') +
                    ' звук при достижении 80% HP<br>' +
                    this.getSelectSound('sound100HP') +
                    ' звук при достижении 100% HP' +
                    this.getGitHubLink('regeneration') +
                    '<span style="margin-left: 15px;">идея: ' +
                    '<a href="https://' + general.domain +
                    '/info.php?id=73295" style="font-weight: bold;" ' +
                    'target="_blank">W_or_M</a></span>', '56'],
                ['Дополнение для панели навигации',
                    'Добавляет возможность установить дополнительные ссылки ' +
                    'в панель навигации.' +
                    this.getGitHubLink('additionForNavigationBar'), '1'],
                ['Подсветка персонажей из ЧС', 'Подсвечивает ссылки на ' +
                    'персонажей, входящих в черный список на всех страницах ' +
                    'игры. Делает неактивной ссылку принятия боя c ' +
                    'персонажем из черного списка в одиночных боях.<br><br>' +
                    '&nbsp;&nbsp;<a target="_blank" href=' +
                    '"https://' + general.domain + '/home.friends.php">' +
                    'Запомнить черный список</a> (скрипт должен быть включен)' +
                    '<br><input type="checkbox" id="blockBLOne2One" ' +
                    'disabled> блокировать ссылку принятия боя с персонажем ' +
                    'из ЧС в одиночных заявках' +
                    this.getGitHubLink('blacklistHighlighting'), '4'],
                ['Работа, письмо, посылка, граната, слом', 'Окончание ' +
                    'работы, осталось времени работать, почта, посылка, ' +
                    'присутствие гранаты на поясе, проверка сломанных вещей. ' +
                    'На все события оповещения звуковые и визуальные.<br><b>' +
                    'P.S.</b> Гранатомет в руках расценивается как ' +
                    'присутствие гранаты.<br><br><input type="checkbox" ' +
                    'id="showwork" disabled /> отображать время работы<br>' +
                    '<input type="checkbox" id="showsms" disabled /> ' +
                    'отображать получение почты/посылки<br><input ' +
                    'type="checkbox" id="showbroken" disabled /> отображать ' +
                    'наличие сломанных предметов<br><input type="checkbox" ' +
                    'id="showgren" disabled /> отображать отсутствие гранаты ' +
                    'на поясе<br>Звук при получении почты: ' +
                    this.getSelectSound('soundSms') + '<br>Звук ' +
                    '"Пора работать": &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' +
                    '&nbsp;&nbsp;&nbsp;' + this.getSelectSound('soundWork') +
                    this.getGitHubLink('workPostGrenadesBroken'), '5'],
                ['Ресурсы и бонусы', 'Создает ссылки "Ресурсы" и "Бонусы" ' +
                    'вверху страницы. При клике выводятся соответствующие ' +
                    'данные.' + this.getGitHubLink('resourcesAndBonuses'), '6'],
                ['Удаление личных сообщений', 'Добавляет сылку "Удалить ' +
                    'отмеченные" вверху страниц входящих и исходящих ' +
                    'сообщений. Отметка синдикатных рассылок и сообщений от ' +
                    'робота.<br><br><input id="syndmail" type="checkbox" ' +
                    'disabled /> отмечать синдовые рассылки<br>' +
                    '<input id="robotmail" type="checkbox" disabled /> ' +
                    'отмечать рассылки от робота<br>' +
                    '<input id="importantmail" type="checkbox" disabled /> ' +
                    'НЕ отмечать письма с пометкой "важное"' +
                    this.getGitHubLink('deleteSms'), '8'],
                ['Подтверждение нажатия "Удалить и забанить"', 'Красит ' +
                    'кнопку "Удалить и забанить" при просмотре личных ' +
                    'сообщений в розовый цвет. При нажатии требует ' +
                    'подтверждения операции.' +
                    this.getGitHubLink('delAndAddBlackSms'), '54'],
                ['Таймер для выполнения квестов NPC', 'На главной странице ' +
                    'выводит время, оставшееся до взятия квеста и сcылку на ' +
                    'NPC, у которого в последний раз брали квест. Звуковое ' +
                    'оповещение. Умеет выводить список NPC с информацией ' +
                    'о них для каждого острова.<br>Звук "Пора делать ' +
                    'квест": ' + this.getSelectSound('soundTimerNPC') +
                    this.getGitHubLink('timeNpc'), '12'],
                ['Упаковка одинаковых предметов в инвентаре', 'Упаковка ' +
                    'одинаковых предметов в инвентаре.' +
                    this.getGitHubLink('inventoryPlus'), '15'],
                ['Счетчик Гб', 'Показывает измененние количества Гб на ' +
                    'главной странице персонажа.' +
                    this.getGitHubLink('gbCounter'), '17'],
                ['Новости и приглашения в синдикаты', 'Выделение и мигание ' +
                    'приглашений в синдикаты, новых и не прочитанных ' +
                    'новостей на главной странице персонажа.' +
                    this.getGitHubLink('newsAndInvit'), '20'],
                ['Исправление умелок вида +-xxx', 'Исправляет умелки вида ' +
                    '+-xxx, полученные при выполнении квестов на странице ' +
                    'информации персонажа и на главной странице.' +
                    this.getGitHubLink('fixSkills'), '24'],
                ['Просмотр истории личных сообщений', 'При просмотре ' +
                    'входящего/исходящего сообщения устанавливает ссылку ' +
                    'для вывода предыдущей переписки с персонажем. Так же ' +
                    '"умеет" выводить ссылки на тексты сообщений, доступные ' +
                    'для официальных синдикатов в хронологическом порядке.' +
                    this.getGitHubLink('historySms'), '26'],
                ['Результативность игры в рулетку, покер, тотализатор',
                    'Анализ результативности игры в рулетку, тотализатор, ' +
                    'покер и заработанных денег в боях на странице ' +
                    'информации персонажа.' +
                    this.getGitHubLink('gameMania'), '28'],
                ['Порты и терминалы', 'Показывает на карте местонахождение ' +
                    'терминалов и портов.' +
                    this.getGitHubLink('portsAndTerminals'), '37'],
                ['Дальность оружия', 'Добавляет дальность оружия на странице ' +
                    'информации любого персонажа.' +
                    this.getGitHubLink('rangeWeapon'), '38'],
                ['Изменение Вашей кармы', 'При изменении Вашей кармы выводит ' +
                    'сообщение на странице информации персонажа.' +
                    this.getGitHubLink('scanKarma'), '40'],
                ['Извещения о входе персонажа в игру', 'Выдает сообщение ' +
                    'и/или звуковой сигнал при появлении (или выходе) в ' +
                    'онлайне определенного персонажа.' +
                    this.getGitHubLink('scanPers'), '41'],
                ['Поиск персонажа', 'Добавляет форму поиска персонажа.' +
                    this.getGitHubLink('searchUser'), '43'],
                ['Счетчики опыта и умений', 'Счетчики опыта и умений на ' +
                    'главной странице персонажа.' +
                    this.getGitHubLink('skillCounters'), '44'],
                ['Быстрый просмотр Ваших достижений', 'Добавляет ссылку ' +
                    '"Достижения" в верхней части страниц игры при нажатии ' +
                    'на которую выводятся Ваши ачивки, но только те, ' +
                    'которые были отмечены на <a target="_blank" ' +
                    'href="https://' + general.domain + '/info.ach.php?id=' +
                    general.myID + '">странице достижений</a>.' +
                    this.getGitHubLink('showMyAchievements') +
                    '<span style="margin-left: 15px;">идея: ' +
                    '<a href="https://' + general.domain + '/info.php?' +
                    'id=134292" style="font-weight: bold;" target="_blank">' +
                    'Горыныч</a></span>', '47'],
                ['Время до возможности выставить карму', 'На странице ' +
                    'информации персонажа показывает динамический счетчик ' +
                    'времени до возможности поставить карму.' +
                    this.getGitHubLink('timeKarma'), '50'],
                ['Подсветка профессий', 'При наличии у персонажа лицензии ' +
                    'киллера, боевика или наемника название профессии на его ' +
                    'странице информации окрашивается в красный цвет.' +
                    this.getGitHubLink('profColor'), '57'],
                ['Текущий мини-квест на странице информации персонажа',
                    'Вывод текущего ужедневного мини-квеста на странице ' +
                    'информации персонажа.' +
                    this.getGitHubLink('currentQuestOnInfo') +
                    '<span style="margin-left: 15px;">идея: ' +
                    '<a href="https://' + general.domain + '/info.php?' +
                    'id=54662" style="font-weight: bold;" target="_blank">' +
                    'kaa</a></span>', '58'],
                ['Кнопки для вставки bb-кодов цитирования и наклонного шрифта',
                    'В формах для отправки личных сообщений и сообщений на ' +
                    'форуме добавляет кнопки для вставки в позиции курсора ' +
                    'bb-кодов для цитирования [q][/q] и наклонного шрифта ' +
                    '[i][/i]' +
                    this.getGitHubLink('bbCodeInMessages'), '62'],
                ['Счетчик опыта профессий',
                    'На странице информации персонажа отображает оставшееся ' +
                    'количество очков до следующего уровня професии.' +
                    this.getGitHubLink('professionLevels') +
                    '<span style="margin-left: 15px;">идея: ' +
                    '<a href="https://' + general.domain + '/info.php?' +
                    'id=285394" style="font-weight: bold;" target="_blank">' +
                    'Bodyarm</a></span>', '63']],

            'Персональный NPC': [
                ['Оповещения', 'Если личный NPC ожидает распоряжений и его ' +
                    'здоровье более 79%, то на главной странице персонажа ' +
                    'ссылка на NPC начинает "пульсировать". Если NPC ' +
                    'находится на Аутленде и его здоровье менее 30%, то фон ' +
                    'ссылки становится розовый. Звуковые оповещения. Статус ' +
                    'NPC проверяется один раз в 10 секунд, перезагрузки ' +
                    'главной страницы персонажа не требуется.<br><br>' +
                    'Звук при ожидании распоряжений: ' +
                    this.getSelectSound('soundPersNPC1') +
                    '<br>Звук, если здоровье на Ауте < 30%: ' +
                    this.getSelectSound('soundPersNPC2') +
                    this.getGitHubLink('personalNPCNotifications'), '35']],

            'Бои': [
                ['Дополнение для боев', 'Расширенная информация в списке ' +
                    'выбора противника + сортировка списка по номеру, ' +
                    'дальности, уровню, видимости и т.д. Динамический центр, ' +
                    'продвинутое расположение бойцов на поле боя в бою и в ' +
                    'режиме наблюдения за боем, кнопка "Сказать ход", ' +
                    'чекбоксы "Говорить только правую руку", "Говорить ' +
                    'только левую руку", "Сказать своей команде", "Сказать ' +
                    'как координатор". Быстрая вставка ника в поле чата (при ' +
                    'клике на "конвертике" рядом с никами бойцов или при ' +
                    'двойном клике на изображении бойца на схеме поле боя). ' +
                    'Информация вверху страницы о набитом HP, вашем ' +
                    'здоровье, видимости и т.д. При одиночном клике по ' +
                    'противнику на схеме поля боя происходит его выбор в ' +
                    'качестве цели. Кнопка "Обновить". Подсвечивает зеленым ' +
                    'цветом тех персонажей, которые уже сделали ход. Выводит ' +
                    'общее количество персонажей и количество персонажей ' +
                    'сделавших ход. Таймаут обновления заявки после входа в ' +
                    'нее и таймаут обновления данных в бою.<br>' +
                    '<span style="color: #0000FF;">Параметры в настройках ' +
                    'персонажа для правильной работы скрипта</span>: ' +
                    '(<a href="' + general.mainPath + 'imgs/AdvBattleAll/' +
                    'screen1.png" target="_blank">скриншот</a>)<br>' +
                    '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' +
                    '- оформление боя в desktop-версии игры: упрощенное<br>' +
                    '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' +
                    '- расположение в бою: примитивное<br>' +
                    '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' +
                    '- JavaScript-версия: использовать.<br><br>' +
                    'Показывать чекбокс "Сказать как координатор" ' +
                    '(вставка \'!*\' в поле чата боя): ' +
                    '<input type="checkbox" id="showCoordButton" disabled />' +
                    '<br><br>При нажатии на кнопку "Сказать ход":<br>' +
                    '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' +
                    '<input type="checkbox" ' +
                        'id="notWriteEnemyNameWhenSayMove" disabled /> ' +
                    'не выводить имя противника<br>' +
                    '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' +
                    '<input type="checkbox" ' +
                        'id="notWriteSkillsWhenSayMove" disabled /> ' +
                    'не выводить применяемые навыки' +
                    '<br><br>Таймаут обновления данных в бою:' +
                    '<span style="margin-left: 54px;"> </span>' +
                    '<input id="refreshBattle" maxlength="2" style="width: ' +
                    '30px;" value="' + (general.getData(4)[0] || '0') +
                    '" disabled /> сек (0 - настройки игры по умолчанию)<br>' +
                    'Таймаут обновления заявки при входе в нее: ' +
                    '<input id="refreshAppl" maxlength="2" style="width: ' +
                    '30px;" value="' + (general.getData(4)[1] || '0') +
                    '" disabled /> сек (0 - настройки игры по умолчанию)<br>' +
                    'Звук при начале боя:&nbsp;&nbsp;&nbsp;&nbsp;' +
                    this.getSelectSound('advBattleSound1') + '<br>' +
                    'Звук при начале хода:&nbsp;&nbsp;' +
                    this.getSelectSound('advBattleSound2') +
                    this.getGitHubLink('advBattleAll'), '3'],
                ['Ссылки в логе боя, критические выстрелы', 'В бою и на ' +
                    'страницax логов боев делает все ники персонажей ' +
                    'ссылками. Показывает критические выстрелы вашего ' +
                    'персонажа и их общее количество (опционально).<br><br>' +
                    '<input type="checkbox" id="showcrits" disabled /> ' +
                    'показывать критические выстрелы' +
                    this.getGitHubLink('critShotsAndLinksBtlLog'), '7'],
                ['Счетчик боев', 'Показывает общее количество боев, побед и ' +
                    'поражений за текущие сутки на страницax протоколов ' +
                    'боев.' + this.getGitHubLink('countBattles'), '16'],
                ['Счетчик времени до начала синдикатного боя', 'Динамический ' +
                    'счетчик времени до начала синдикатного боя, звуковое ' +
                    'оповещение.<br><br><input id="timeSyndSoundLimit" ' +
                    'value="' + (general.getData(28)[0] || '90') +
                    '" maxlength="3" style="width: 35px;" /> - время до боя ' +
                    '(сек), когда будет проигран второй звук ' +
                    '<span style="color: #FF0000;">(не менее 15)</span><br>' +
                    this.getSelectSound('syndSoundBattle1') + ' - звук, если ' +
                    'осталось более <span name="sptssl"></span> сек<br>' +
                    this.getSelectSound('syndSoundBattle2') + ' - звук, если ' +
                    'осталось менее <span name="sptssl"></span> сек' +
                    this.getGitHubLink('soundSyndBattle'), '52'],
                ['Фильтр общих боев', 'Фильтр общих боев по уровню бойцов.' +
                    this.getGitHubLink('commonBattleFilter'), 59],
                ['Фильтр по оружию в одиночных заявках', 'Фильтр по оружию в ' +
                    'одиночых заявках. Фильтр по уровням и типу оружия, ' +
                    'встроенный в игре, переносится вверх страницы. Все ' +
                    'настройки находятся на <a target="_blank" ' +
                    'href="https://' + general.domain + '/warlist.php?' +
                    'war=armed">странице одиночных заявок</a>' +
                    this.getGitHubLink('filterWarlistOne2One'), '23'],
                ['Ссылки на странице одиночных заявок', 'На странице ' +
                    'заявок одиночных боев делает ники вызывающих на бой ' +
                    'персонажей ссылками на них.' +
                    this.getGitHubLink('linksInOne2One'), '33'],
                ['Информация о вызывающем Вас на бой персонаже в одиночных ' +
                    'боях', 'Выводит информацию о  вызывающем Вас на бой ' +
                    'персонаже в одиночных боях (HP, дальность оружия, ' +
                    'умения, ссылки-изображения на экипировку, бонусы). ' +
                    'Звуковое оповещение при вызове.<br><br>Звук при вызове: ' +
                    this.getSelectSound('soundOne2One') +
                    this.getGitHubLink('one2OneCallerInfo'), '34'],
                ['Контроль Уранa и ЭC', 'Сортировка объектов по типу, ' +
                    'островам и контролирующим синдикатам на странице ' +
                    '<a target="_blank" href="https://' + general.domain +
                    '/info.realty.php?id=2">ГосЭнегоАтом</a>' +
                    this.getGitHubLink('gosEnergoAtomFilter'), '29'],
                ['Проверка сектора после боя', 'Выводит сообщение после боя, ' +
                    'если персонаж находится не в секторе со своим домом и ' +
                    'его здоровье менее 80%.' +
                    this.getGitHubLink('housHealth'), '31'],
                ['Изображения покемонов', 'В боях с покемонами и в режиме ' +
                    'наблюдения за боем (Ejection Point, Overlord Point, ' +
                    'прибрежная зона) показывает изображения для каждого ' +
                    'пока.' + this.getGitHubLink('imgPokemonsOnBattle') +
                    '<span style="margin-left: 15px;">идея: ' +
                    '<a href="https://' + general.domain + '/info.php?' +
                    'id=436429" style="font-weight: bold;" target="_blank">' +
                    'Buger_man</a></span>', '51'],
                ['Таймер для боев за порты', 'Вывод точного/оставшегося ' +
                    'времени до боя за порт в верхней части страницы.' +
                    this.getGitHubLink('portTimer') +
                    '<span style="margin-left: 15px;">идея: ' +
                    '<a href="https://' + general.domain + '/info.php?' +
                    'id=205482" style="font-weight: bold;" target="_blank">' +
                    'Enemy333</a></span>', '36'],
                ['Знаки синдикатов на страницах списков ближайших/прошедших ' +
                    'боев за порты', 'На страницах списков ближайших/' +
                    'прошедших боев за порты добавляет знаки синдикатов, ' +
                    'являющиеся ссылками на их онлайн.' +
                    this.getGitHubLink('portsSyndLinks'), '61']],

            'Синдикаты': [
                ['Сортировка на странице онлайна синдиката', 'Сортировка ' +
                    'онлайна синдиката и союза по идущим боям. Выделение ' +
                    'синдикатных боев.' +
                    this.getGitHubLink('sortSyndOnline'), '30'],
                ['Анализ активности синдиката', 'Анализ активности ' +
                    'синдиката. Рейтинг нападающих, баланс Гб и PTS ' +
                    'контролируемой недвижимости, кто и сколько взял/положил ' +
                    'Гб на счет синдиката, изменения состава.' +
                    this.getGitHubLink('syndAnalyser'), '46'],
                ['Анализ расхода PTS', 'Анализ расхода PTS синдиката. ' +
                    'Сортировка данных по купленным гранатам, чипам, ' +
                    'выданным званиям и знакам, общему количеству PTS.' +
                    this.getGitHubLink('syndPtsAnalyser'), '45'],
                ['Онлайн основного синдиката и союза на главной странице',
                    'На главной странице персонажа добавляет ссылки на его ' +
                    'основной синдикат и союз (если есть), при нажатии на ' +
                    'которые выводится онлайн синдиката со ссылками отправки ' +
                    'сообщения каждому бойцу. Если персонаж в бою, то ссылка ' +
                    'красного цвета. Так же добавляются конвертики для ' +
                    'отправки сообщений в разделах "Мои друзья" и "Гости".' +
                    this.getGitHubLink('syndOnlineOnMainPage'), '49']],

            'Форум': [
                ['Отображение сообщения, на которое отвечают', 'В ответе на ' +
                    'сообщение показывает то сообщение, на которое ' +
                    'отвечают.' + this.getGitHubLink('showInitMessOnForum'),
                    '42'],
                ['Отметки сообщений на форуме и другое.', 'Звуковые и ' +
                    'визуальные оповещения при появлении новых тем или смене ' +
                    'верхней темы, новых сообщений в темах, скрытие закрытых ' +
                    'тем и прикрепленок, отметка закрытых тем, скрытие ' +
                    'ненужных веток форума.' +
                    this.getGitHubLink('advForum'), '53']],

            'Торговля': [
                ['Ссылки в HighTech магазине для подачи объявлений',  'В ' +
                    'HighTech магазине добавляет ссылки "Продать" и "Купить" ' +
                    'для каждого предмета, при нажатии на которые, выводится ' +
                    'форма подачи объявления на ДО для данного предмета.' +
                    this.getGitHubLink('buyHightech'), '19'],
                ['Фильтр ресурсов на странице экономической статистики',
                    'Фильтр ресурсов на <a target="_blank" ' +
                    'href="https://' + general.domain + '/stats.php">' +
                    'странице экономической статистики</a><br><br>Введите ' +
                    'названия ресурсов через запятую, которые будут ' +
                    'отображаться на вышеуказанной странице. Например: ' +
                    'Уран,Водоросли,Маковая соломка,Трава,Батареи<br>' +
                    '<input id="filter_res" style="width: 350px;" disabled />' +
                    this.getGitHubLink('filterResOnStat'), '22'],
                ['Ссылки на High-tech вооружение в государственном магазине',
                    'В государственном магазине рядом со ссылками на типы ' +
                    'вооружения добавляет ссылки на вооружение High-tech' +
                    this.getGitHubLink('linksToHighTech') +
                    '<span style="margin-left: 15px;">идея: ' +
                    '<a href="https://' + general.domain + '/info.php?' +
                    'id=436429" style="font-weight: bold;" target="_blank">' +
                    'Buger_man</a></span>', '27'],
                ['Форма аренды и продажи', 'При передаче предмета в аренду ' +
                    'форма передачи выделяется голубым цветом. Если предмет ' +
                    'продается или передается в постоянное пользование, то ' +
                    'красным. Если указана нулевая цена, выводится сообщение ' +
                    'с подтверждением продолжения операции.' +
                    this.getGitHubLink('rentAndSale'), '39']],

            'Ферма': [
                ['Производственный опыт и прибыль', 'Отображение ' +
                    'производственного опыта и прибыли в Гб за один час для ' +
                    'каждого растения.' +
                    this.getGitHubLink('farmExperience'), '9'],
                ['Таймер', 'Таймер для фермы. Звуковое оповещение когда ' +
                    'пора полить/собрать.<br><br>' +
                    this.getSelectSound('farmTmSound') + ' - звук когда пора ' +
                    'поливать/собирать (0 - без звука)<br>' +
                    '<input id="farmtmSndIntrvl" maxlength="3" ' +
                    'style="width: 40px;" disabled /> - интервал повторения ' +
                    'звука в секундах (0 - не повторять)' +
                    this.getGitHubLink('farmTimer'), '10'],
                ['Все растения на одной странице, счетчики', 'Счетчик гб и ' +
                    'производственного опыта на ферме. Для каждого растения ' +
                    'присутствует изображение, производственный опыт и ' +
                    'прибыль (общие и в 1 час), цена, время созревания в ' +
                    'минутах и часах.<br><br>' +
                    '<input id="showGbFarmCounter" type="checkbox" ' +
                    'disabled /> показывать счетчик Гб<br>' +
                    '<input id="showExpFarmCounter" type="checkbox" ' +
                    'disabled /> показывать счетчик производственног опыта' +
                    this.getGitHubLink('allPlantsOnFarm'), '13'],
                ['Играем без фермы', 'Убирает ссылку на ферму на главной ' +
                    'странице и на странице информации персонажа.' +
                    this.getGitHubLink('fuckTheFarm'), '25']]
        }; // 2}}}

        /**
         * @method showHideScriptInfo // {{{2
         */
        this.showHideScriptInfo = function () {
            var _this = this,
                info = _this.parentNode.lastElementChild;

            if (info.style.display) {
                info.style.display = '';
                _this.innerHTML = '[&minus;]';
            } else {
                info.style.display = 'none';
                _this.innerHTML = '[+]';
            }
        }; // 2}}}

        /**
         * @method onOffScript // {{{2
         */
        this.onOffScript = function () {
            var _this = this,
                ind = /chk(\d+)/.exec(_this.id)[1],
                hiddenDiv = _this.nextElementSibling,
                inp = hiddenDiv.querySelectorAll('input'),
                sel = hiddenDiv.querySelectorAll('select'),
                i;

            initScript[ind] = _this.checked ? '1' : '';
            general.setData(initScript, 1);

            // выкл/вкл элементы управления настройками
            for (i = 0; i < inp.length; i++) {
                inp[i].disabled = !_this.checked;
            }

            for (i = 0; i < sel.length; i++) {
                sel[i].disabled = !_this.checked;
            }
        }; // 2}}}

        /**
         * @method checkScriptUpdate // {{{2
         */
        this.checkScriptUpdate = function () {
            var url = 'https://' + general.domain + '/info.php?id=2095458';
            new AjaxQuery().init(url, 'GET', null, true, function (xml) {
                var ver = /cpingw:(\d+\.\d+-\d+)/.exec(xml.responseText);
                if (ver && ver[1] !== general.version) {
                    general.$('linkNewVerScript').style.
                        visibility = 'visible';
                    general.$('refreshVer').innerHTML = '(' + ver[1] + ')';
                }
            }, null);
        }; // 2}}}

        /**
         * @method setSettingsForAdvBattleAll // {{{2
         */
        this.setSettingsForAdvBattleAll = function () {
            var _this = this,
                ind = _this.id === 'refreshBattle' ? 0 : 1,
                data = general.getData(4);

            data[ind] = new CheckInputText().init(_this, 0) ?
                    _this.value : '';
            general.setData(data, 4);
        }; // 2}}}

        /**
         * @method testSound // {{{2
         */
        this.testSound = function () {
            var _this = this;
            new PlaySound().init(_this.previousElementSibling.value);
        }; // 2}}}

        /**
         * @method modifyData // {{{2
         * @param   {int}       ind
         * @param   {int}       ind1
         * @param   {String}    val
         */
        this.modifyData = function (ind, ind1, val) {
            var tmp = general.getData(ind);
            tmp[ind1] = val;
            general.setData(tmp, ind);
        }; // 2}}}

        /**
         * @method init // {{{2
         */
        this.init = function () {
            general.doc.title = 'CPIGW :: Настройки';
            // noinspection HtmlUnknownAttribute
            var tdStyle = ' style="background-color: #E0FFE0;">',
                gwImgUrl = 'https://images.' +
                    general.domain.replace('www.', '') + '/i/home/',
                str = '<table style="width: 100%; box-shadow: 8px 10px 7px ' +
                    'rgba(122,122,122,0.5);"><tr><td ' + tdStyle +
                    '<table style="width: 100%;"><tr><td style="width: 23%;">' +
                    '<span style="color: #8C5B07; font-weight: bold;">' +
                    'Настройки:</span><img id="imgSaveSettings" ' +
                    'style="margin-left: 5px; cursor: pointer;" ' +
                    'src="' + gwImgUrl + 'ganjafile.gif" title="Сохранить" ' +
                    'alt="Сохранить" /><img id="imgRestoreSettings" ' +
                    'style="cursor: pointer; margin-left: 5px;" ' +
                    'src="' + gwImgUrl + 'cashlog.gif" title="Восстановить" ' +
                    'alt="Восстановить" /><img id="imgResetSettings" ' +
                    'style="cursor: pointer; margin-left: 5px;" ' +
                    'src="' + gwImgUrl + 'questlog.gif" title="Сбросить" ' +
                    'alt="Сбросить"  /></td>' +
                    '<td style="font-size: 8pt; text-align: center;">' +
                    '<a id="linkNewVerScript" target="_blank" ' +
                    'style="color: #FF0000; visibility: hidden;" href="' +
                    general.mainPath + '_' + general.selfName + '.user.js">' +
                    'Доступна новая версия</a> ' +
                    '<span id="refreshVer"></span></td>' +
                    '<td style="font-size: 7pt; width: 35%; text-align: ' +
                    'right;"><a target="_blank" style="opacity: 0.5; ' +
                    'text-decoration: none; font-size: 7pt;" ' +
                    'href="https://' + general.domain + '/info.php?' +
                    'id=2095458"><span style="color: #F90332;">developed by' +
                    '</span> <span style="color: #014305; font-weight: 700;">' +
                    'MyRequiem©</span></a> ' + general.version +
                    '<a target="_blank" title="История изменений" ' +
                    'href="' + general.mainPath + 'ChangeLog.txt" ' +
                    'style="text-decoration: none; margin: 0 5px 0 3px;">' +
                    '<img src="' + gwImgUrl + 'wlog.gif" border="0" ' +
                    'width="12" height="10" tile="Лог" alt="Лог" /></a></td>' +
                    '</tr><tr id="trRestoreSettings" style="display: none;">' +
                    '<td colspan="3"><span style="color: #C00000;">' +
                    'Восстановление настроек. Вставьте ранее сохраненную ' +
                    'строку настроек и нажмите "Восстановить"</span>:' +
                    '<br><input id="inpRestoreSettings" ' +
                    'style="width: 97%;" /><br>' +
                    '<input id="butRestoreSettings" type="button" ' +
                    'value="Восстановить" /></td></tr>' +
                    '<tr id="trSaveSettings" style="display: none;">' +
                    '<td colspan="3"><span style="color: #C00000;">' +
                    'Выделите &lt;Ctrl+A&gt; и сохраните &lt;Ctrl+C&gt; ' +
                    'содержимое поля для последующего восстановления ' +
                    'настроек</span>:<textarea id="textAreaSaveSettings" ' +
                    'cols="90" rows="7" readonly="true" style="resize: none; ' +
                    'width: 100%;"></textarea></td></tr></table></td></tr>',
                groupStyle = ' style="background-color: #D0EED0; text-align: ' +
                    'center; color: #990000;"><b>',
                spanStyle = ' style="cursor: pointer;">',
                hiddenDivStyle = ' style="display: none; padding: 0 10px ' +
                    '0 50px; background-color: #E7E7E7">',
                prop,
                i;

            // формирование таблицы настроек
            for (prop in this.infoScripts) {
                if (this.infoScripts.hasOwnProperty(prop)) {
                    str += '<tr><td' + groupStyle + prop + '</b></td></tr>';

                    for (i = 0; i < this.infoScripts[prop].length; i++) {
                        str += '<tr><td' + tdStyle + '<span' + spanStyle +
                            '[+]</span> ' + '<input id="chk' +
                            this.infoScripts[prop][i][2] +
                            '" type="checkbox" /> ' +
                            this.infoScripts[prop][i][0] + '<div' +
                            hiddenDivStyle + this.infoScripts[prop][i][1] +
                            '</div></td></tr>';
                    }
                }
            }

            str += '</table>';
            var settingsContainer = general.doc.querySelector('tr>td.txt' +
                    '[valign="top"]');
            settingsContainer.innerHTML = '<div></div>';
            settingsContainer = settingsContainer.firstElementChild;
            settingsContainer.setAttribute('style', 'margin: 10px 0 20px 0');
            settingsContainer.innerHTML = str;

            // проверка обновлений
            this.checkScriptUpdate();

            // обрабочики открытия/закрытия скрытых контейнеров описания
            // скрипта, обработчики чекбоксов и установка значений чекбоксов
            var spans = settingsContainer.
                    querySelectorAll('span[style="cursor: pointer;"]'),
                chkid,
                chk;

            for (i = 0; i < spans.length; i++) {
                spans[i].addEventListener('click',
                        this.showHideScriptInfo, false);
                chk = spans[i].nextElementSibling;
                chk.addEventListener('click', this.onOffScript, false);
                chkid = +(/chk(\d+)/.exec(chk.getAttribute('id')))[1];
                if (initScript[chkid]) {
                    chk.click();
                }
            }

            general.$('imgRestoreSettings').addEventListener('click',
                function () {
                    var trRestoreSettings = general.$('trRestoreSettings'),
                        trSaveSettings = general.$('trSaveSettings');

                    if (!trSaveSettings.style.display) {
                        trSaveSettings.style.display = 'none';
                    }

                    trRestoreSettings.style.display = trRestoreSettings.
                        style.display ? '' : 'none';
                }, false);

            general.$('imgSaveSettings').addEventListener('click', function () {
                var trSaveSettings = general.$('trSaveSettings'),
                    trRestoreSettings = general.$('trRestoreSettings');

                if (trSaveSettings.style.display) {
                    trRestoreSettings.style.display = 'none';
                    trSaveSettings.style.display = '';
                    general.$('textAreaSaveSettings').value = general.st.
                        getItem(general.STORAGENAME);
                } else {
                    trSaveSettings.style.display = 'none';
                }
            }, false);

            var butRestoreSettings = general.$('butRestoreSettings'),
                inpRestoreSettings = general.$('inpRestoreSettings');

            butRestoreSettings.addEventListener('click', function () {
                var val = inpRestoreSettings.value;
                if (!val) {
                    alert('Введите строку настроек');
                    return;
                }

                general.st.setItem(general.STORAGENAME, val);
                general.root.location.reload();
            }, false);

            inpRestoreSettings.addEventListener('keypress', function (e) {
                var ev = e || general.root.event;
                if (ev.keyCode === 13 || ev.keyCode === 10) {
                    butRestoreSettings.click();
                }
            }, false);

            general.$('imgResetSettings').addEventListener('click',
                function () {
                    if (confirm('Сброс настроек !!!\nВы уверены ???')) {
                        general.st.removeItem(general.STORAGENAME);
                        general.root.location.reload();
                    }
                }, false);

            // AdvBattleAll
            // обработчики текстовых полей модуля дополнений для боев
            general.$('refreshBattle').
                addEventListener('input',
                        this.setSettingsForAdvBattleAll, false);
            general.$('refreshAppl').
                addEventListener('input',
                        this.setSettingsForAdvBattleAll, false);

            var checkInputText = new CheckInputText().init,
                _this = this;

            // Показывать чекбокс "Сказать как координатор"
            // (вставка '!*' в поле чата боя)
            var showCoordButton = general.$('showCoordButton');
            showCoordButton.checked = general.getData(4)[25] === '1';
            showCoordButton.addEventListener('click', function () {
                _this.modifyData(4, 25, showCoordButton.checked ? '1' : '');
            }, false);

            // ###
            // # При нажатии на кнопку "Сказать ход"
            // ###
            // чекбокc "не выводить имя противника"
            var notWriteEnemyName = general.$('notWriteEnemyNameWhenSayMove');
            notWriteEnemyName.checked = general.getData(4)[27] === '1';
            notWriteEnemyName.addEventListener('click', function () {
                _this.modifyData(4, 27, notWriteEnemyName.checked ? '1' : '');
            }, false);
            // чекбокc "не выводить применяемые навыки"
            var notWriteSkills = general.$('notWriteSkillsWhenSayMove');
            notWriteSkills.checked = general.getData(4)[28] === '1';
            notWriteSkills.addEventListener('click', function () {
                _this.modifyData(4, 28, notWriteSkills.checked ? '1' : '');
            }, false);

            // выбор звука начала боя и начала хода
            var advBattleSound1 = general.$('advBattleSound1'),
                advBattleSound2 = general.$('advBattleSound2');
            advBattleSound1.value = general.getData(4)[21] || '0';
            advBattleSound1.addEventListener('change', function () {
                _this.modifyData(4, 21, advBattleSound1.value === '0' ?
                        '' : advBattleSound1.value);
            }, false);
            advBattleSound2.value = general.getData(4)[22] || '0';
            advBattleSound2.addEventListener('change', function () {
                _this.modifyData(4, 22, advBattleSound2.value === '0' ?
                        '' : advBattleSound2.value);
            }, false);
            general.$('ladvBattleSound1').
                addEventListener('click', this.testSound, false);
            general.$('ladvBattleSound2').
                addEventListener('click', this.testSound, false);

            // чекбокс настроек подсветки персонажей из черного списка
            // (блокировать или нет ссылку принятия боя в одиночках)
            var chkBL = general.$('blockBLOne2One');
            chkBL.checked = general.getData(5)[1] === '1';
            chkBL.addEventListener('click', function () {
                _this.modifyData(5, 1, chkBL.checked ? '1' : '');
            }, false);

            // работа, слом, грена, почта/посылка
            // кнопки прослушать звук
            general.$('lsoundSms').
                addEventListener('click', this.testSound, false);
            general.$('lsoundWork').
                addEventListener('click', this.testSound, false);

            // установка списка выбора звуков sms и "Пора работать"
            var soundSms = general.$('soundSms');
            soundSms.value = general.getData(6)[6] || '0';
            // обработчики списков выбора звука
            soundSms.addEventListener('change', function () {
                _this.modifyData(6, 6, soundSms.value === '0' ?
                        '' : soundSms.value);
            }, false);
            var soundWork = general.$('soundWork');
            soundWork.value = general.getData(6)[7] || '0';
            soundWork.addEventListener('change', function () {
                _this.modifyData(6, 7, soundWork.value === '0' ?
                        '' : soundWork.value);
            }, false);
            // чекбоксы настроек
            var showWork = general.$('showwork');
            showWork.checked = general.getData(6)[2];
            showWork.addEventListener('click', function () {
                _this.modifyData(6, 2, showWork.checked ? '1' : '');
            }, false);
            var showSms = general.$('showsms');
            showSms.checked = general.getData(6)[3];
            showSms.addEventListener('click', function () {
                _this.modifyData(6, 3, showSms.checked ? '1' : '');
            }, false);
            var showBroken = general.$('showbroken');
            showBroken.checked = general.getData(6)[4];
            showBroken.addEventListener('click', function () {
                _this.modifyData(6, 4, showBroken.checked ? '1' : '');
            }, false);
            var showGren = general.$('showgren');
            showGren.checked = general.getData(6)[5];
            showGren.addEventListener('click', function () {
                _this.modifyData(6, 5, showGren.checked ? '1' : '');
            }, false);

            // ссылки в логе боя, критические выстрелы
            var showCrits = general.$('showcrits');
            showCrits.checked = general.getData(7)[0];
            showCrits.addEventListener('click', function () {
                general.setData(showCrits.checked ? '1' : '', 7);
            }, false);

            // удаление личных сообщений
            var syndMail = general.$('syndmail');
            syndMail.checked = general.getData(8)[0];
            syndMail.addEventListener('click', function () {
                _this.modifyData(8, 0, syndMail.checked ? '1' : '');
            }, false);
            var robotMail = general.$('robotmail');
            robotMail.checked = general.getData(8)[1];
            robotMail.addEventListener('click', function () {
                _this.modifyData(8, 1, robotMail.checked ? '1' : '');
            }, false);
            var importantMail = general.$('importantmail');
            importantMail.checked = general.getData(8)[2];
            importantMail.addEventListener('click', function () {
                _this.modifyData(8, 2, importantMail.checked ? '1' : '');
            }, false);

            // FarmTimer
            general.$('lfarmTmSound').
                addEventListener('click', this.testSound, false);
            var farmTmSound = general.$('farmTmSound');
            farmTmSound.value = general.getData(9)[4] || '0';
            farmTmSound.addEventListener('change', function () {
                _this.modifyData(9, 4, farmTmSound.value === '0' ?
                        '' : farmTmSound.value);
            }, false);
            var farmTmSoundInterval = general.$('farmtmSndIntrvl');
            farmTmSoundInterval.value = general.getData(9)[5] || '0';
            farmTmSoundInterval.addEventListener('input', function () {
                _this.modifyData(9, 5, checkInputText(farmTmSoundInterval, 0) ?
                        farmTmSoundInterval.value : '60');
            }, false);

            // TimeNpc
            general.$('lsoundTimerNPC').
                addEventListener('click', this.testSound, false);
            var soundTimerNPC = general.$('soundTimerNPC');
            soundTimerNPC.value = general.getData(10)[3] || '0';
            soundTimerNPC.addEventListener('change', function () {
                _this.modifyData(10, 3, soundTimerNPC.value === '0' ?
                        '' : soundTimerNPC.value);
            }, false);

            // PersonalNPCNotifications
            general.$('lsoundPersNPC1').
                addEventListener('click', this.testSound, false);
            var soundPersNPC1 = general.$('soundPersNPC1');
            soundPersNPC1.value = general.getData(18)[0] || '0';
            soundPersNPC1.addEventListener('change', function () {
                _this.modifyData(18, 0, soundPersNPC1.value === '0' ?
                        '' : soundPersNPC1.value);
            }, false);

            general.$('lsoundPersNPC2').
                addEventListener('click', this.testSound, false);
            var soundPersNPC2 = general.$('soundPersNPC2');
            soundPersNPC2.value = general.getData(18)[1] || '0';
            soundPersNPC2.addEventListener('change', function () {
                _this.modifyData(18, 1, soundPersNPC2.value === '0' ?
                        '' : soundPersNPC2.value);
            }, false);

            // AllPlantsOnFarm
            var showGbFarmCounter = general.$('showGbFarmCounter');
            showGbFarmCounter.checked = general.getData(11)[4];
            showGbFarmCounter.addEventListener('click', function () {
                _this.modifyData(11, 4, showGbFarmCounter.checked ?
                        '1' : '');
            }, false);
            var showExpFarmCounter = general.$('showExpFarmCounter');
            showExpFarmCounter.checked = general.getData(11)[5];
            showExpFarmCounter.addEventListener('click', function () {
                _this.modifyData(11, 5, showExpFarmCounter.checked ?
                        '1' : '');
            }, false);

            // FilterResOnStat
            var filtRes = general.$('filter_res');
            filtRes.value = general.getData(15)[0];
            filtRes.addEventListener('input', function () {
                general.setData(filtRes.value, 15);
            }, false);

            // One2OneCallerInfo
            general.$('lsoundOne2One').
                addEventListener('click', this.testSound, false);
            var soundOne2One = general.$('soundOne2One');
            soundOne2One.value = general.getData(21)[0] || '0';
            soundOne2One.addEventListener('change', function () {
                _this.modifyData(21, 0, soundOne2One.value === '0' ?
                        '' : soundOne2One.value);
            }, false);

            // SoundSyndBattle
            if (!general.getData(28)[0]) {
                this.modifyData(28, 0, '90');
            }
            var timeSyndSoundLimit = general.$('timeSyndSoundLimit'),
                sptssl = general.doc.querySelectorAll('span[name="sptssl"]');
            sptssl[0].innerHTML = general.getData(28)[0];
            sptssl[1].innerHTML = general.getData(28)[0];
            timeSyndSoundLimit.addEventListener('input', function () {
                _this.modifyData(28, 0, checkInputText(timeSyndSoundLimit, 15) ?
                        timeSyndSoundLimit.value : '90');
                sptssl[0].innerHTML = general.getData(28)[0];
                sptssl[1].innerHTML = general.getData(28)[0];
            }, false);

            var syndSoundBattle1 = general.$('syndSoundBattle1'),
                syndSoundBattle2 = general.$('syndSoundBattle2');
            syndSoundBattle1.value = general.getData(28)[1] || '0';
            syndSoundBattle1.addEventListener('change', function () {
                _this.modifyData(28, 1, syndSoundBattle1.value === '0' ?
                        '' : syndSoundBattle1.value);
            }, false);
            syndSoundBattle2.value = general.getData(28)[2] || '0';
            syndSoundBattle2.addEventListener('change', function () {
                _this.modifyData(28, 2, syndSoundBattle2.value === '0' ?
                        '' : syndSoundBattle2.value);
            }, false);
            general.$('lsyndSoundBattle1').
                addEventListener('click', this.testSound, false);
            general.$('lsyndSoundBattle2').
                addEventListener('click', this.testSound, false);

            // Regeneration
            var sound80HP = general.$('sound80HP'),
                sound100HP = general.$('sound100HP');
            sound80HP.value = general.getData(31)[0] || '0';
            sound80HP.addEventListener('change', function () {
                _this.modifyData(31, 0, sound80HP.value === '0' ?
                        '' : sound80HP.value);
            }, false);
            sound100HP.value = general.getData(31)[1] || '0';
            sound100HP.addEventListener('change', function () {
                _this.modifyData(31, 1, sound100HP.value === '0' ?
                        '' : sound100HP.value);
            }, false);
            general.$('lsound80HP').
                addEventListener('click', this.testSound, false);
            general.$('lsound100HP').
                addEventListener('click', this.testSound, false);
        }; // 2}}}
    }; // 1}}}

    /**
     * @lends ShowMainSettings.prototype {{{1
     */
    ShowMainSettings.prototype = {
        /**
         * @method getGitHubLink
         * @param   {String}    scriptName
         * @return  {String}
         */
        getGitHubLink: function (scriptName) {
            var gitHubPage = 'https://github.com/MyRequiem/' +
                    general.selfName + '/tree/master/separatedScripts/';

            return '<br><a target="_blank" href="' + gitHubPage +
                scriptName[0].toUpperCase() + scriptName.substr(1) +
                '" style="color: #0000FF;">' + scriptName + '.user.js</a>';
        }
    }; // 1}}}

    /**
     * @class SetPoints {{{1
     * @constructor
     */
    var SetPoints = function () {
        /**
         * @method init
         * @param   {String|int}    num
         * @param   {String}        separator
         * @param   {Boolean}       flagSign
         * @return  {String}
         */
        this.init = function (num, separator, flagSign) {
            var x = +num,
                sign = x > 0 && flagSign ? '+' : '',
                i;

            if (isNaN(x)) {
                return 'NaN';
            }

            x = x.toString().split('').reverse();
            for (i = 2; i < x.length; i += 3) {
                if (x[i] === '-' || !x[i + 1] || x[i + 1] === '-') {
                    break;
                }

                x[i] = separator + x[i];
            }

            return sign + x.reverse().join('');
        };
    }; // 1}}}

    /**
     * @class GetPos {{{1
     * @constructor
     */
    var GetPos = function () {
        /**
         * @method init
         * @param   {Object}   obj
         * @return  {Object}
         */
        this.init = function (obj) {
            var _obj = obj,
                x = 0,
                y = 0;

            while (_obj) {
                x += _obj.offsetLeft;
                y += _obj.offsetTop;
                _obj = _obj.offsetParent;
            }

            return {x: x, y: y};
        };
    }; // 1}}}

    /**
     * @class NotGiveCannabisLeaf {{{1
     * @constructor
     */
    var NotGiveCannabisLeaf = function () {
        /**
         * @method  changeFavicon
         */
        this.changeFavicon = function () { // {{{2
            var head = general.doc.querySelector('head');

            if (head) {
                var linkTags = head.querySelectorAll('link[rel*="icon"]'),
                    i;

                for (i = 0; i < linkTags.length; i++) {
                    head.removeChild(linkTags[i]);
                }

                var link = general.doc.createElement('link');
                link.setAttribute('type', 'image/x-icon');
                link.setAttribute('rel', 'shortcut icon');
                link.setAttribute('href', 'https://raw.githubusercontent.' +
                    'com/MyRequiem/comfortablePlayingInGW/master/imgs/' +
                    'NotGiveCannabisLeaf/favicon.ico');
                head.appendChild(link);
            }
        }; // 2}}}

        /**
         * @method  changeIcons {{{2
         */
        this.changeIcons = function () {
            var imgPath = general.imgPath + 'NotGiveCannabisLeaf/',
                imgOn = imgPath + 'on.gif',
                imgOff = imgPath + 'off.gif',
                imgs = general.doc.querySelectorAll('img'),
                src,
                i;

            for (i = 0; i < imgs.length; i++) {
                src = imgs[i].getAttribute('src');
                if (/\/i\/gon\.gif|\/info\.online\.php\?id=/.test(src)) {
                    imgs[i].setAttribute('src', imgOn);
                } else if (/\/i\/goff\.gif/.test(src)) {
                    imgs[i].setAttribute('src', imgOff);
                }
            }
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            this.changeFavicon();
            // noinspection JSUnresolvedVariable
            if (!/\/news\.php\?set=1/.test(general.loc)) {
                this.changeIcons();
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class AdditionForNavigationBar {{{1
     * @constructor
     */
    var AdditionForNavigationBar = function () {
        /**
         * @property navigPanel
         * @type {HTMLElement|null}
         */
        this.navigPanel = null;
        /**
         * @property divMain
         * @type {HTMLDivElement|null}
         */
        this.divMain = null;

        /**
         * @method addLink {{{2
         * @param   {Node}  link
         */
        this.addLink = function (link) {
            // добавление в панель
            var target = this.navigPanel.
                    lastElementChild.previousSibling;
            this.navigPanel.insertBefore(general.doc.createTextNode(' | '),
                    target);
            this.navigPanel.insertBefore(link, target);

            // добавление ссылки и кнопы ее удаления в основной div
            var divLink = general.doc.createElement('div');

            var linkClone = link.cloneNode(true);
            linkClone.style.fontSize = '9pt';
            divLink.appendChild(linkClone);

            var delLinkButton = general.doc.createElement('span');
            delLinkButton.setAttribute('style', 'margin-left: 2px; ' +
                    'cursor: pointer; font-size: 7pt;');
            delLinkButton.innerHTML = '[x]';
            divLink.appendChild(delLinkButton);

            this.divMain.insertBefore(divLink,
                    this.divMain.lastElementChild);

            // обработчик кнопы удаления ссылки
            var _this = this;
            delLinkButton.addEventListener('click', function () {
                var thisLink = this,
                    linkName = thisLink.previousElementSibling.innerHTML,
                    allPanelLinks = _this.navigPanel.querySelectorAll('a'),
                    i;

                // удаляем ссылку из панели
                for (i = 0; i < allPanelLinks.length; i++) {
                    if (allPanelLinks[i].innerHTML === linkName) {
                        _this.navigPanel.removeChild(allPanelLinks[i].
                            previousSibling);
                        _this.navigPanel.removeChild(allPanelLinks[i]);
                        break;
                    }
                }

                // удаляем ссылку из div'а
                _this.divMain.removeChild(thisLink.parentNode);

                // удаляем запись из хранилища
                var dataSt = JSON.parse(general.getData(2)[0]),
                    temp = {},
                    name;

                for (name in dataSt) {
                    if (dataSt.hasOwnProperty(name)) {
                        if (name === linkName) {
                            continue;
                        }

                        temp[name] = dataSt[name];
                    }
                }

                general.setData(JSON.stringify(temp), 2);
            }, false);
        }; // 2}}};

        /**
         * @method createLink {{{2
         * @param   {String}    name
         * @param   {Array}     attr    href, style
         * @return  {HTMLElement}
         */
        this.createLink = function (name, attr) {
            var link = general.doc.createElement('a');
            link.setAttribute('style', 'color: #669966; text-decoration: ' +
                    'none; font-size: 7pt;' + attr[1]);
            link.innerHTML = name;
            link.href = attr[0];
            return link;
        }; // 2}}}

        /**
         * @method clearFields {{{2
         */
        this.clearFields = function () {
            general.$('lname').value = '';
            general.$('lhref').value = '';
            general.$('lstyle').value = '';
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            this.navigPanel = general.DESIGN_VERSION === 'v2' ?
                    general.doc.querySelector('div[style="position: ' +
                        'relative; left: 0; top: 0; width:100%; ' +
                        'font-size:7pt;color:#669966;"] ' +
                        'center:first-child') :
                    general.doc.querySelector('td[style="font-size:7pt;' +
                        'color:#669966;"]');

            if (!this.navigPanel) {
                return;
            }

            var dataSt = general.getData(2)[0];
            if (!dataSt) {
                dataSt = '{}';
                general.setData(dataSt, 2);
            }

            dataSt = JSON.parse(dataSt);

            // добавляем в панель кнопу для создания ссылки
            var addLinkButton = general.doc.createElement('span');
            addLinkButton.setAttribute('style', 'cursor: pointer;');
            addLinkButton.innerHTML = '+';
            this.navigPanel.appendChild(general.doc.createTextNode(' | '));
            this.navigPanel.appendChild(addLinkButton);

            var _this = this;
            // обработчик открытия/закрытия div'а
            addLinkButton.addEventListener('click', function () {
                _this.divMain.style.display = _this.divMain.style.display ?
                        '' : 'none';
                _this.clearFields();
            }, false);

            // div для добавления ссылок
            var divAddLink = general.doc.createElement('div');
            divAddLink.setAttribute('style', 'margin-top: 5px;');
            divAddLink.innerHTML = 'Название:<br><input id="lname" ' +
                'maxlength="20" style="width: 237px;" /><br>' +
                'Ссылка:<br><input id="lhref" style="width: 237px;" />' +
                '<br>Стиль, например: "color: blue;"<br>' +
                '<input id="lstyle" style="width: 237px;" />' +
                '<br><span id="set_link" style="cursor: pointer; color: ' +
                '#0000FF;">Добавить</span><span id="hide_nav_div" ' +
                'style="cursor: pointer; margin-left: 20px; color: ' +
                '#FF0000;">Закрыть</span>';
            this.divMain = general.doc.createElement('div');
            this.divMain.appendChild(divAddLink);

            var pos = new GetPos().init(addLinkButton);
            this.divMain.setAttribute('style', 'position: absolute; ' +
                'display: none; border: 1px #339933 solid; background: ' +
                '#F0FFF0; width: 240px; font-size: 8pt; padding: 3px; ' +
                'left: ' + (pos.x - 260) + '; top: ' + (pos.y + 12) + ';');
            general.doc.body.appendChild(this.divMain);

            // добавляем ссылки из хранилища в панель и в div
            var linkName, lnk;
            for (linkName in dataSt) {
                // noinspection JSUnresolvedVariable
                if (dataSt.hasOwnProperty(linkName)) {
                    lnk = this.createLink(linkName, dataSt[linkName]);
                    this.addLink(lnk);
                }
            }

            // кнопа закрытия div'а
            general.$('hide_nav_div').addEventListener('click',
                function () {
                    _this.clearFields();
                    _this.divMain.style.display = 'none';
                }, false);

            // обработчик кнопы добавления ссылки
            general.$('set_link').addEventListener('click', function () {
                var name = general.$('lname').value,
                    href = general.$('lhref').value,
                    style = general.$('lstyle').value;

                if (!name || !href) {
                    alert('Не верно введены данные');
                    return;
                }

                var allPanelLinks = _this.navigPanel.querySelectorAll('a'),
                    i;

                for (i = 0; i < allPanelLinks.length; i++) {
                    if (allPanelLinks[i].innerHTML === name) {
                        alert('Ссылка с таким названием уже существует');
                        return;
                    }
                }

                // создаем ссылку и втыкаем ее в панель и в div
                var newLink = _this.createLink(name, [href, style]);
                _this.addLink(newLink);

                // добавляем данные в хранилище
                var dtSt = JSON.parse(general.getData(2)[0]);
                dtSt[name] = [href, style];
                general.setData(JSON.stringify(dtSt), 2);

                _this.clearFields();
            }, false);
        }; // 2}}};
    }; // 1}}}

    /**
     * @class AdvBattleAll {{{1
     * @constructor
     */
    var AdvBattleAll = function () {
        /**
         * @property inpTextChat
         * @type {HTMLInputElement|null}
         */
        this.inpTextChat = null;
        /**
         * @property myInfoTopPanel
         * @type {HTMLTableCellElement|null}
         */
        this.myInfoTopPanel = null;
        /**
         * @property tooltip
         * @type {HTMLDivElement|null}
         */
        this.tooltip = null;
        // noinspection JSUnusedGlobalSymbols
        /**
         * @property intervalUpdateInpTextChat
         * @type {int}
         */
        this.intervalUpdateInpTextChat = 0;
        /**
         * @property sayMoveButton
         * @type {HTMLInputElement|null}
         */
        this.sayMoveButton = null;
        /**
         * @property enemies
         * @type {Object|null}
         */
        this.enemies = null;
        /**
         * @property leftRightCommands
         * @type {Array}
         */
        this.leftRightCommands = [];
        /**
         * @property allFighters
         * @type {Object}
         */
        // объекты всех бойцов на поле
        // {"name": {"hp": x, ...}, "name": {...}, ... }
        this.allFighters = {};
        /**
         * @property leftPers
         * @type {Array|null}
         */
        this.leftPers = null;
        /**
         * @property rightPers
         * @type {Array|null}
         */
        this.rightPers = null;
        /**
         * @property myPers
         * @type {Object|null}
         */
        this.myPers = null;
        /**
         * @property imgPath
         * @type {String}
         */
        this.imgPath = general.imgPath + 'AdvBattleAll/';
        // noinspection JSUnusedGlobalSymbols
        /**
         * @property tmRefreshBattle
         * @type {int}
         */
        this.tmRefreshBattle = 0;
        // noinspection JSUnusedGlobalSymbols
        /**
         * @property tmHighlightPers
         * @type {int}
         */
        this.tmHighlightPers = 0;
        // noinspection JSUnusedGlobalSymbols
        /**
         * @property graphTable
         * @type {HTMLTableElement|null}
         */
        this.graphTable = null;
        // noinspection JSUnusedGlobalSymbols
        /**
         * @property checkSound
         * @type {Boolean}
         */
        this.checkSound = true;
        /**
         * @property weapon {{{2
         * @type {Object}
         */
        this.rangeWeapon = {
            // Пистолеты
            'magnum': '1',
            'pm': '0',
            'rbcat': '0',
            'ttgun': '0',
            'eagle': '0',
            // Штурмовые винтовки гос
            'ak47': '2',
            'schmeisser': '2',
            'm16': '2',
            'ak_74': '2',
            'l1a1': '4',
            'aks': '0',
            'hk53': '4',
            'sg541': '6',
            'xm8': '6',
            'steyr': '7',
            'trw': '8',
            'sig': '10',
            'stg44': '8',
            'm14': '10',
            'sar': '12',
            'cz_sa': '12',
            'g3': '10',
            'fara83': '12',
            'g3aa': '10',
            'arx160': '12',
            'sr88': '12',
            'fnfal': '12',
            'm82': '13',
            'fs2000': '14',
            'fnscar': '15',
            'aps95': '16',
            'bofors': '16',
            'm17s': '18',
            'hk417': '20',
            'vektor': '22',
            'sw_m203': '20/1',
            'tiger': '24',
            'soar': '28',
            'sw_gl1': '26/2',
            'hk762': '21',
            'enfield80': '26',
            'cz805': '28',
            'm468': '29',
            // Штурмовые винтовки арт
            'g11': '4',
            'oicw': '6',
            'an94': '10',
            'f2000': '12',
            'fnfnc': '13',
            'hk_416': '14',
            'groza': '14',
            'ka90': '15',
            'taiga': '16',
            'xcr': '17',
            'tkb517': '18',
            'steyr_a3': '20',
            'ak103': '20',
            'g36': '22',
            'g41': '24',
            'sig552': '26',
            'sw_gp34': '24/1',
            'ace': '28',
            'sc2010': '32',
            'sw_m6': '28/3',
            'colt692': '24',
            'thales': '30',
            // встроенный подствол:
            // дальность оружия, подствола и радиус поражения подствола
            'thales_grl': '30/28/1',
            'rec7': '32',
            'sigmcx': '34',
            // Штурмовые винтовки аренда
            'colt_extreme': '26',
            'lr300': '30',
            'sig750': '34',
            // Снайперское оружие гос
            'svdmini': '4',
            'l96': '8',
            '149s': '8',
            'cz527': '10',
            'm40': '12',
            'police': '14',
            'mauser': '12',
            'remington700': '14',
            'psg': '15',
            'ssg': '14',
            'm76': '16',
            'svd': '16',
            'om50': '16',
            'ssg2000': '16',
            'b94': '15',
            'ssg3000': '18',
            'falcon': '16',
            'f2': '18',
            'm24': '20',
            'savage10fp': '18',
            'steyr_iws': '20',
            'ulr338': '20',
            'savage100fp': '22',
            'tikka': '24',
            'cz700': '26',
            'bora': '26',
            'ksv': '28',
            'amr2': '30',
            'fd308': '25',
            'r11': '28',
            'bor762': '30',
            'ultima': '30',
            'gepard': '31',
            // Снайперское оружие арт
            'barret': '18',
            'bfg': '20',
            'tactical600': '20',
            'pgm': '18',
            'm89sr': '20',
            'm107': '20',
            'vssk': '22',
            'rt20': '22',
            'cs5': '22',
            'barret_xm500': '24',
            'm85': '26',
            'steyr_ste': '28',
            'rangemaster': '28',
            'mauser93': '30',
            'sateravi': '34',
            'ar10': '28',
            'thor': '32',
            'awm': '34',
            'm110': '34',
            'msr': '35',
            // Снайперское оружие аренда
            'blaser_93': '30',
            'blaser-t': '32',
            'sharpshooter': '36',
            // Пистолеты-пулемёты гос
            'uzi': '0',
            'calico': '0',
            'lf57': '1',
            'ump': '0',
            'gg95': '3',
            'm4': '4',
            'mas38': '4',
            'kashtan': '8',
            'suomi': '8',
            'ingram': '8',
            'colt': '8',
            'walther': '10',
            'fmk3': '10',
            'vihr': '10',
            'saf': '7',
            'mpi81': '10',
            'agram': '12',
            'bizon': '14',
            'kedr': '16',
            'colt636': '20',
            'scorpionevo': '22',
            'berettamx4': '24',
            'fmg9': '26',
            'gilboa': '28',
            'pmx': '30',
            // Пистолеты-пулемёты арт
            'mp5': '4',
            'berettam12': '10',
            'scorpion': '12',
            'stkinetics': '10',
            'p90': '12',
            '90m1': '13',
            'mp7': '15',
            'kriss2': '22',
            'mtar21': '24',
            'pm06': '26',
            'uzipro': '28',
            'apc9': '30',
            'mpx': '32',
            // Пулемёты гос
            'fn_min': '1',
            'lewis': '2',
            'type95': '2',
            'fnmag': '4',
            'bren': '6',
            'l86': '4',
            'm16a2': '6',
            'mg3': '6',
            'type62': '6',
            'ar70': '6',
            'p41': '6',
            'saw': '6',
            'zb53': '7',
            'nsv': '6',
            'type67': '7',
            'galil': '7',
            'sig710': '8',
            'm60': '8',
            'vickers': '10',
            'vz59': '12',
            'mg4': '12',
            'mg36': '12',
            'm61': '14',
            'aat': '14',
            'xm312': '16',
            'sturm': '18',
            'sw_ubl37': '20/1',
            'venom': '20',
            'zastava': '24',
            'fort401': '16',
            'vektorss77': '22',
            'xm806': '24',
            'kord': '26',
            // Пулемёты арт
            'pkp': '6',
            'ameli': '8',
            'hk21': '8',
            'rpk74': '10',
            'pkm': '9',
            'm16lmg': '10',
            'aa52': '11',
            'mg43': '13',
            'pssg': '14',
            'ares16': '14',
            'spitfire': '15',
            'pkms': '16',
            'minigun762': '18',
            'mg50': '20',
            'ultimax': '22',
            'sw_agc': '25/1',
            'shrike': '24',
            'mg5': '28',
            'ng7': '20',
            'lwmmg': '26',
            'hk121': '28',
            'ukm': '30',
            // Пулемёты аренда
            'minigun_heavy': '24',
            'sigmg': '28',
            'hk221': '32',
            // Дробовики гос
            'winchester1200': '0',
            'hunter': '0',
            'remington': '2',
            'sgun2': '3',
            'm37': '4',
            'toz': '7',
            'jackhammer': '1',
            'spas12': '0',
            'striker': '2',
            'saiga': '10',
            'rmb93': '4',
            'neostead': '6',
            'xm26': '20',
            'hawk': '2',
            'benelli': '12',
            'liberator': '16',
            'spas15': '18',
            'r8': '22',
            'sw_gl1_sg': '26/2',
            'versamax': '22',
            'fnslp': '24',
            'srm1216': '26',
            // Дробовики арт
            'mossberg': '12',
            'vepr': '12',
            'mag7': '7',
            'usas12': '14',
            'sw_xgl40_sg': '22/1',
            'ksg': '18',
            'usas15': '22',
            'uts15': '26',
            'sw_m6_sg': '28/3',
            'fabarm': '28',
            'origin12s': '30',
            'xtr12': '32',
            // Гранатометы гос
            'rpg': '5/1',
            'ptrk': '12/2',
            'glauncher': '8/1',
            'grg': '10/1',
            'paw20': '12/2',
            'rpgu': '14/3',
            'grom2': '10/1',
            'ags30': '16/1',
            'gm94': '8/2',
            'gl06': '8/2',
            'gmg': '17/2',
            'balkan': '20/2',
            'rg6': '24/2',
            'm202': '20/5',
            'mm1': '24/4',
            // Гранатометы арт
            'mk47': '27/3',
            'm32': '28/4',
            // Гранаты гос
            'r10': '20/2',
            'emp_irs': '10/0',
            'emp_a': '20/0',
            'rgd5': '5/1',
            'grenade_f1': '3/2',
            'rgd2': '10/0',
            'lightst': '10/0',
            'lights': '10/0',
            'rkg3': '6/0',
            'mdn': '5/3',
            'rgd2m': '10/0',
            'rgo': '6/2',
            'm84': '16/0',
            'rgn': '8/0',
            'emp_ir': '10/0',
            'fg3l': '6/2',
            'l83a1': '20/0',
            'emp_s': '10/0',
            'm67': '8/0',
            'm3': '8/2',
            'hg78': '10/1',
            'hg84': '12/1',
            'fg6': '10/1',
            'anm14': '8/1',
            'm34ph': '8/2',
            'fg7': '12/1',
            'fg8bd': '16/2',
            // Гранаты синдовые
            'lightsm': '20/0',
            'grenade_dg1': '20/0',
            'ghtb': '12/1',
            'lightss': '12/0',
            'rgd2s': '15/0',
            'fg5': '6/2',
            'molotov': '6/1',
            'hellsbreath': '10/1',
            'napalm': '10/2',
            'me85': '17/2',
            // Гранаты лут
            'old_rgd5': '5/1',
            // Специальное оружие
            'rogatka': '0',
            'saw_airsoft': '0',
            'pb_marker': '2',
            'potato2': '0',
            // Турели
            'heavysg': '60',
            'tavor': '60',
            'larue': '60',
            'ga2gl': '60',
            // другое
            '11y_revolver': '0'
        }; // 2}}};
        /**
         * @property art {{{2
         * @type {Array}
         */
        this.art = [
            'g11', 'oicw', 'an94', 'f2000', 'fnfnc', 'hk_416', 'groza', 'ka90',
            'taiga', 'xcr', 'tkb517', 'steyr_a3', 'ak103', 'g36', 'g41',
            'sig552', 'sw_gp34', 'ace', 'sc2010', 'sw_m6', 'colt692', 'thales',
            'thales_grl', 'rec7', 'sigmcx', 'pkp', 'ameli', 'hk21', 'rpk74',
            'pkm', 'm16lmg', 'aa52', 'mg43', 'pssg', 'ares16', 'spitfire',
            'pkms', 'minigun762', 'mg50', 'ultimax', 'sw_agc', 'shrike',
            'mg5', 'ng7', 'lwmmg', 'hk121', 'ukm', 'barret', 'bfg',
            'tactical600', 'pgm', 'm89sr', 'm107', 'vssk', 'rt20', 'cs5',
            'barret_xm500', 'm85', 'steyr_ste', 'rangemaster', 'mauser93',
            'sateravi', 'ar10', 'thor', 'awm', 'm110', 'msr', 'mp5',
            'berettam12', 'scorpion', 'stkinetics', 'p90', '90m1', 'mp7',
            'kriss2', 'mtar21', 'pm06', 'uzipro', 'apc9', 'mpx', 'mossberg',
            'vepr', 'mag7', 'usas12', 'ksg', 'usas15', 'uts15', 'sw_m6_sg',
            'fabarm', 'origin12s', 'xtr12', 'mk47', 'm32', 'bronik3c',
            'bronik4c', 'bronik5c', 'bronik6c', 'blackhawk', 'armour_p300',
            'blackcell', 'rbr', 'armour_patrol', 'delta5', 'mr1_armour',
            'delta7', 'fav', 'protector', 'sandsarmour', 'ar500', 'lwhelmet',
            'empires', 'sas_helmet', 'chelmet', 'spectra', 'arhelmet',
            'fasthelmet', 'helmetmk6', 'mpas', 'g15helmet', 'ksfhelmet',
            'predatorhelmet', 'mwshelmet', 'lowshieldc', 'cboots', 'shields_la',
            'hboots', 'dboots', 'swatboots', 'cobraboots', 'officerboots',
            'bootspec', 'spiderboots', 'dwboots', 'sandsboots', 'maskl',
            'rockycamo', 'predator', 'mesh', 'forester', 'jackpyke', 'swatcamo',
            'deltamask', 'woodsman', 'ilight', 'deye', 'nighthawk', 'atn14',
            'edge', 'nvg1', 'pvs21', 'irgs1', 'nyx7', 'nokia9500', 'fieldcomp',
            'attackbelt', 'ammobelt', 'bomberbelt', 'mealpack', 'eaglebag',
            'cbelt', 'commandobelt', 'loadbelt', 'armybag', 'fan', 'brelok',
            'cigar', 'clocks', 'gift_wallet', 'gift_watch', 'lighter',
            'saperka3', 'watch_ganjarmani', 'armyknife', 'uniknife', 'nrs2',
            'gt_multitool', 'ctactical', 'flask_gt', 'nokia9500', 'n81', 'n95',
            'armygps', 'gwatch', 'ganjapad', 'radio_srx', 'gwsat45',
            'tacticalgps', 'oldcompass', 'bottleopener', 'ganjacup', 'pendant',
            'flashlight', 'heartglasses', 'radio_gw148', 'bors', 'ganjacola2',
            'mentats2', 'minimedikit', 'medikit', 'bigmedikit', 'slr', 'apache',
            'mi8', 'cadillac', 'chinook', 'harley', 'ch148', 'irvs12', 'jscamo',
            'tactilight', 'gwtab7', 'triforce_xb', 'gcombatflask'
        ]; // 2}}}
        /**
         * @property rent {{{2
         * @type {Array}
         */
        this.rent = [
            'minigun_heavy', 'sigmg', 'hk221', 'colt_extreme', 'lr300',
            'sig750', 'blaser_93', 'blaser-t', 'sharpshooter', 'irlights',
            'mask_hawster', 'mask_hawster1', 'nokia8800', 'nokia8800_se',
            'tesla_armour'
        ]; // 2}}}

        /**
         * @metod getRandom1to3 {{{2
         * @return  {int}
         */
        this.getRandom1to3 = function () {
            return Math.round(Math.random() * 1000) % 3 + 1;
        }; // 2}}}

        /**
         * @method clearSavedStrokeAfterSay {{{2
         */
        this.clearSavedStrokeAfterSay = function () {
            var dataSt = general.getData(4);
            dataSt[11] = '';
            dataSt[12] = '';
            dataSt[13] = '';
            dataSt[14] = '';
            dataSt[15] = '';
            dataSt[16] = '';
            dataSt[19] = '';
            dataSt[20] = '';
            dataSt[24] = '';
            general.setData(dataSt, 4);
        }; // 2}}}

        /**
         * @metod sayMove {{{2
         * @param   {Object}    _this
         * @param   {Boolean}   fake
         */
        this.sayMove = function (_this, fake) {
            /**
             * fake - если нажали <Enter> в поле ввода или кнопку "Написать",
             *  (т.е. отправляем обычное сообщение), то реально не говорим ход,
             *  а просто сохраняем его для восстановления после отправки
             *  сооощения.
             */

            // ход сделан
            if (/Ждём ход противника/i.test(general.$('bf').innerHTML)) {
                return;
            }

            // куда отходим
            var def = general.doc.querySelector('input[type="radio"]' +
                    '[name="defence"]:checked'),
                dataSt = general.getData(4);

            dataSt[14] = def ? /\d/.exec(def.id)[0] : _this.getRandom1to3();

            // подходим или нет
            dataSt[16] = general.doc.querySelector('input[type="checkbox"]' +
                    '[name="walk"]:checked') ? '1' : '';

            // номер противника
            var enemyList = general.$('euids').querySelectorAll('option'),
                reg = /^(\d+)\. .*\[\d+ \/ \d+\] ([^:]+):/,
                enemy,
                tmps,
                i;

            for (i = 0; i < enemyList.length; i++) {
                if (enemyList[i].selected) {
                    tmps = enemyList[i].innerHTML;
                    // ecли пок то будет
                    // 1. Electrode [Major][20] 182 HP - 13!
                    enemy = reg.exec(tmps) || /^(\d+)\. ([^\s]+)/.exec(tmps);

                    break;
                }
            }

            dataSt[11] = enemy[1];

            // общий навык
            var generalSkill = '';
            if (general.doc.querySelector('input[type="checkbox"]' +
                    '[name="apm_activate"]:checked')) {
                dataSt[19] = general.doc.querySelector('label[for="apmid"]').
                    innerHTML;
                if (!dataSt[28]) {
                    generalSkill = ' + ' + dataSt[19];
                }
            }

            // навык специалиста
            var specialSkill = '';
            if (general.doc.querySelector('input[type="checkbox"]' +
                    '[name="aps_activate"]:checked')) {
                dataSt[20] = general.doc.querySelector('label[for="apsid"]').
                    innerHTML;
                if (!dataSt[28]) {
                    specialSkill = ' + ' + dataSt[20];
                }
            }

            var str = '~';

            // граната
            var isGren = false;
            if (general.doc.querySelector('input[type="checkbox"]' +
                    '[name="use_grenade"]:checked')) {
                str += general.doc.querySelector('label[for="bagaboom"]').
                    innerHTML.replace(/: бросить/, '');
                dataSt[15] = '1';

                if (!fake) {
                    _this.inpTextChat.value = str + ' в ' + enemy[1] +
                        (!dataSt[27] ? ' [' + enemy[2] + ']' : '') +
                            generalSkill + specialSkill;
                }

                isGren = true;
            }

            // подствол
            var isLauncher = false;
            if (general.doc.querySelector('input[type="checkbox"]' +
                    '[name="subweapon_shot"]:checked')) {
                str += 'Подствол в ';
                dataSt[24] = '1';
                isLauncher = true;
            }

            var leftAttack = general.doc.querySelector('input[type="radio"]' +
                '[name^="left_attack"]:checked'),
                rightAttack = general.doc.querySelector('input[type="radio"]' +
                    '[name^="right_attack"]:checked');

            dataSt[12] = leftAttack ? /\d/.exec(leftAttack.id)[0] : '';
            dataSt[13] = rightAttack ? /\d/.exec(rightAttack.id)[0] : '';

            general.setData(dataSt, 4);

            if (!isGren) {
                str += enemy[1];

                // правая рука
                // (если не установлен чекбокс "Говорить только левую руку")
                if (!isLauncher && dataSt[13] && !dataSt[18]) {
                    str += dataSt[13] === '1' ? ' ле' :
                            dataSt[13] === '2' ? ' ц' : ' пр';
                }

                // левая рука
                // (если не установлен чекбокс "Говорить только правую руку")
                if (!isLauncher && dataSt[12] && !dataSt[23]) {
                    str += dataSt[12] === '1' ? ' ле' :
                            dataSt[12] === '2' ? ' ц' : ' пр';
                }

                if (!fake) {
                    _this.inpTextChat.value = str +
                        (!dataSt[27] ? ' [' + enemy[2] + ']' : '') +
                            generalSkill + specialSkill;
                }
            }

            // отправляем сообщение в чат
            general.doc.querySelector('input[type="submit"]' +
                '[value="Написать"]').click();
        }; // 2}}};

        /**
         * @method getLeftRightCommands {{{2
         */
        this.getLeftRightCommands = function () {
            if (this.leftRightCommands.length) {
                return;
            }

            if (general.viewMode) {
                this.leftRightCommands.push(general.doc.
                        querySelector('tr>td[valign="top"][width="15%"]' +
                            ':first-child'));
                this.leftRightCommands.push(general.doc.
                        querySelector('tr>td[valign="top"][width="15%"]' +
                            ':last-child'));
                return;
            }

            // в бою ищем DIV'ы с бойцами явно, т.к.они меняются местами по ID
            this.leftRightCommands.push(general.doc.
                    querySelector('#listleft,#listright'));
            this.leftRightCommands[1] =
                this.leftRightCommands[0].id === 'listleft' ?
                        general.$('listright') : general.$('listleft');
        }; // 2}}}

        /**
         * @method getPers {{{2
         * @param   {HTMLElement}   obj
         * @return  {NodeList}
         */
        this.getPers = function (obj) {
            var pers = obj.querySelectorAll('a[href*="/info.php?id="]');
            // поки
            if (!pers.length) {
                pers = [];
                var divs = obj.querySelectorAll('div.customscroll'),
                    i;
                for (i = 0; i < divs.length; i++) {
                    pers.push(divs[i].querySelector('b'));
                }
            }

            return pers;
        }; // 2}}}

        /**
         * @method getDataFighters {{{2
         * @param   {HTMLLinkElement}   persLink
         */
        this.getDataFighters = function (persLink) {
            var prnt = persLink.parentNode.parentNode.lastElementChild,
                objPers = {};

            objPers.lvl = '';
            if (persLink.nextSibling && persLink.nextSibling.textContent) {
                objPers.lvl = persLink.nextSibling.textContent;
            }

            var allText = prnt.textContent;
            objPers.hp = /HP: \d+\/\d+/.test(allText) ?
                    /HP: (\d+)\/(\d+)/.exec(allText) : '';
            objPers.dist = /расстояние: \d+/i.test(allText) ?
                    /расстояние: (\d+)/i.exec(allText)[1] : '';
            objPers.visib = /видимость: \d+%/i.test(allText) ?
                    /видимость: (\d+%)/i.exec(allText)[1] : '';
            objPers.skill = '';

            // номера противников в режиме наблюдения за боем
            if (general.viewMode) {
                var persNum = persLink.parentNode.previousElementSibling;
                objPers.num = persNum ? persNum.innerHTML : null;
            }

            // добавляем умелку
            var skill = prnt.querySelectorAll('img[src*="/skill_"]'),
                skillValue,
                i;

            if (skill.length) {
                objPers.skill = '<br>' +
                    '<span style="color: #0087FF; font-size:9px;">';

                for (i = 0; i < skill.length; i++) {
                    skillValue = skill[i].parentNode &&
                        skill[i].parentNode.nextElementSibling;
                    if (skillValue) {
                        objPers.skill += skillValue.innerHTML +
                            (i !== skill.length - 1 ? ', ' : '');
                    }
                }

                objPers.skill += '</span>';
            }

            // оружие (для заполнения списка выбора врагов)
            objPers.weapon = '';
            // оружие и амуниция
            objPers.allWeapon = '';
            var allAmmunition = prnt.
                    querySelectorAll('a[href*="/item.php?item_id="]'),
                indent = '<span style="font-weight: bold; ' +
                            'margin: 0 3px 0 3px;">&bull;</span>',
                getSw = function (range) {
                    return '<span style="color: #FF0000; font-size: 7pt; ' +
                        'margin-left: 7px; float: right;">(' + range + ')' +
                        '</span>' + indent + '<span style="color: #FF5555;">' +
                        'Подствол</span><br>';
                },
                // для некоторых предметов установка модуля дальности
                // увеличивает дальность больше чем на 1
                modeUpgRange = {
                    'hk762': 3,
                    'colt692': 3,
                    'fd308': 2,
                    'ar10': 2,
                    'saf': 2,
                    'fort401': 3,
                    'ng7': 2,
                    'grom2': 2
                },
                builtinSw = '',
                baseUpgRange,
                isGrenade,
                itemLink,
                itemId,
                range,
                color,
                splt,
                sw;

            // у поков ссылок на амуницию нет
            if (allAmmunition.length) {
                objPers.weapon = allAmmunition[0].innerHTML;
                for (i = 0; i < allAmmunition.length; i++) {
                    itemLink = allAmmunition[i];
                    itemId = /\?item_id=([^&$]+)/.exec(itemLink.href)[1];
                    range = this.rangeWeapon[itemId];
                    color = this.art.indexOf(itemId) !== -1 ? '#009900' :
                                this.rent.indexOf(itemId) !== -1 ? '#870000' :
                                    '#000000';

                    // красим ссылки на странице,
                    // открываем их в отдельной вкладке
                    itemLink.style.color = color;
                    itemLink.setAttribute('target', '_blank');

                    if (range) {
                        // наличие встроенного подствола, например thales_grl
                        builtinSw = '';
                        splt = range.split('/');
                        if (splt.length > 2) {
                            builtinSw = splt[1] + '/' + splt[2];
                        }

                        splt[0] = +splt[0];
                        // установленные модули на дальность
                        baseUpgRange = modeUpgRange[itemId] || 1;
                        if (/&upg=1(&|$)/.test(itemLink.href)) {
                            splt[0] += baseUpgRange;
                        } else if (/&upg=11(&|$)/.test(itemLink.href)) {
                            splt[0] += baseUpgRange + 1;
                        } else if (/&upg=(12|13)(&|$)/.test(itemLink.href)) {
                            splt[0] += 1;
                        }

                        // модификаторы на дальность
                        if (/&m=(27|43)(&|$)/.test(itemLink.href)) {
                            splt[0] += 1;
                        } else if (/&m=(8|16|34)(&|$)/.test(itemLink.href)) {
                            splt[0] += 2;
                        }

                        range = builtinSw ? splt[0] : splt[0] +
                            (splt[1] ? '/' + splt[1] : '');

                        // граната или гранатомет
                        isGrenade = /\//.test(range);
                        objPers.allWeapon += '<span style="color: ' +
                            (isGrenade ? '#870000' : '#0000FF') + '; ' +
                            'font-size: 7pt; margin-left: 7px; ' +
                            'float: right;">(' + range + ')' +
                            '</span>' + indent + '<span style="color: ' +
                            color + ';">' + itemLink.innerHTML + '</span><br>';

                        // подствол (или встроенный, или установленный)
                        if (builtinSw) {
                            objPers.allWeapon += getSw(builtinSw);
                        } else {
                            sw = /&sw=([^&$]+)/.exec(itemLink.href);
                            range = sw ? this.rangeWeapon[sw[1]] : '';
                            if (range) {
                                objPers.allWeapon += getSw(range);
                            }
                        }
                    } else {
                        objPers.allWeapon += indent + '<span style="color: ' +
                            color + ';">' + itemLink.innerHTML + '</span><br>';
                    }
                }
            }

            var name = persLink.textContent.replace(/&amp;/, '&');

            // личные NPC персонажей
            if (/\[NPC\]$/.test(name)) {
                objPers.npc = true;
            }

            this.allFighters[name] = objPers;

            // в бою и если это мой перс, то запоминаем его
            if (!general.viewMode && persLink.href &&
                    persLink.href.indexOf('?id=' + general.myID) !== -1) {
                this.myPers = objPers;
                this.myPers.name = name;
                this.myPers.damage = ['?', '?', '?'];
                var myDamage = prnt.querySelector('img[src$="skull.svg"]');
                if (myDamage && myDamage.parentNode &&
                        myDamage.parentNode.nextElementSibling) {
                    var damage = myDamage.parentNode.nextElementSibling;
                    if (/>(\d+.*(\+\d+)?)\(.*>(\d+)</.test(damage.innerHTML)) {
                        this.myPers.damage = />(\d+.*(\+\d+)?)\(.*>(\d+)</.
                            exec(damage.innerHTML);
                    }
                }
            }
        }; // 2}}}

        /**
         * @method  setPersNikInChat {{{2
         * @param   {Object}    _this
         * @param   {String}    pName
         * @return  {Function}
         */
        this.setPersNikInChat = function (_this, pName) {
            return function () {
                _this.inpTextChat.value = _this.inpTextChat.value + ' ' +
                    pName + ' ';
                _this.inpTextChat.focus();
            };
        }; // 2}}}

        /**
         * @method setNameInChat {{{2
         * @return  {Function}
         */
        this.setNameInChat = function (persName) {
            var _this = this;
            return function () {
                _this.inpTextChat.value += persName + ': ';
                _this.inpTextChat.focus();
            };
        }; // 2}}}

        /**
         * @method  createEnvelopSpan
         * @return  {Element}
         */
        this.createEnvelopSpan = function () {
            var span = general.doc.createElement('span');
            span.setAttribute('name', 'sendmessenv');
            span.innerHTML = ' <img src="' + this.imgPath +
                'envelope.gif" style="width: 15px; cursor: pointer; ' +
                'margin-right: 5px;" alt="img" />';

            return span;
        };

        /**
         * @method setEnvelope {{{2
         */
        this.setEnvelope = function () {
            var mass = [this.leftPers, this.rightPers],
                before,
                span,
                j,
                i;

            for (i = 0; i < 2; i++) {
                for (j = 0; j < mass[i].length; j++) {
                    this.getDataFighters(mass[i][j]);

                    // конвертики покам не нужны
                    if (mass[i][j].nodeName === 'B') {
                        continue;
                    }

                    span = this.createEnvelopSpan();
                    if (!i) {
                        // левая команда
                        mass[i][j].parentNode.appendChild(span);
                    } else {
                        // правая команда
                        before = mass[i][j].parentNode;
                        before.parentNode.
                            insertBefore(span, before.previousElementSibling);
                    }

                    span.querySelector('img').addEventListener('click',
                        this.setNameInChat(mass[i][j].textContent), false);
                }
            }

            // ставим конвертики для наблюдающих за боем
            if (general.viewMode) {
                var observersPanel = general.doc.
                        querySelector('td[align="left"][class="greengraybg"]');

                if (!observersPanel) {
                    return;
                }

                var observers = observersPanel.querySelectorAll('nobr'),
                    observer;

                for (i = 0; i < observers.length; i++) {
                    observer = observers[i].
                        querySelector('a[href*="/info.php?id="]');

                    if (observer) {
                        span = this.createEnvelopSpan();
                        observers[i].appendChild(span);
                        span.querySelector('img').addEventListener('click',
                            this.setNameInChat(observer.textContent),
                                false);
                    }
                }
            }
        }; // 2}}}

        /**
         * @method setMyInfo {{{2
         * @param   {int}   count   количество персонажей сделавших ход
         */
        this.setMyInfo = function (count) {
            // если здоровье меньше максимального, то меняем цвет
            var color = this.myPers.hp[1] === this.myPers.hp[2] ?
                        '#008000' : '#B84906',
                str = '<span style="color: ' + color + ';">' +
                    // HP
                    this.myPers.hp[1] + '</span>/' +
                    '<span style="color: #008000; font-weight: bold;">' +
                    this.myPers.hp[2] + '</span>' +
                    // урон
                    '<span style="margin-left: 15px;">' +
                    this.myPers.damage[1] +
                    '(<span style="color: #009900; font-weight: bold;">' +
                    this.myPers.damage[3] + '</span>)' +
                    // видимость
                    '<span style="margin-left: 15px; font-weight: bold;">' +
                    this.myPers.visib + '</span>' +
                    // количество бойцов
                    '<span style="margin-left: 15px; font-weight: bold;">' +
                    '<span style="color: #FF0000;">' + this.leftPers.length +
                    '</span> / <span style="color: #0000FF;">' +
                    this.rightPers.length + '</span></span>' +
                    // ссылка "Наблюдение"
                    '<a href="https://' + general.domain + '/warlog.php?bid=' +
                    /\?bid=(\d+)/.exec(general.loc)[1] + '&rev=1" ' +
                    'target="_blank" style="margin-left: 15px;"><img src="' +
                    general.imgPath + 'AdvBattleAll/eyes.png" width="16" ' +
                    'height="9" title="Режим наблюдения за боем" alt="img" />' +
                    '</a>';

            // счетчик количества бойцов сделавших ход
            if (count) {
                str += '<span style="margin-left: 15px;">' + count + '/' +
                    (this.leftPers.length + this.rightPers.length) + '</span>';
            }

            this.myInfoTopPanel.innerHTML = str;
        }; // 2}}}

        /**
         * @method sortEnemyList {{{2
         */
        this.sortEnemyList = function () {
            var _this = this;
            // если кнопка уже нажата (выделена жирным)
            if (/bold/.test(_this.getAttribute('style'))) {
                return;
            }

            var id = +/\d/.exec(_this.id)[0],
                i;
            // выделяем жирным на что нажали, остальные обычным шрифтом
            _this.setAttribute('style', 'font-weight: bold;');
            for (i = 0; i < 6; i++) {
                if (i !== id) {
                    general.$('s' + i).setAttribute('style',
                            'cursor: pointer;');
                }
            }

            // записываем данные в хранилище
            var dataSt = general.getData(4);
            dataSt[2] = id.toString();
            general.setData(dataSt, 4);

            // сортируем список по возрастающей
            var regstr = '(\\d+)\\. \\[(\\d+)\\][^\\d]*(\\d+)!? ' +
                    '\\((\\d+)%\\) \\[(\\d+) \\/ (\\d+)\\]',
                reg = new RegExp(regstr),
                select = general.$('euids'),
                countOpt = select.options.length,
                buff,
                opts,
                rez1,
                rez2,
                j;

            for (i = 0; i < countOpt - 1; i++) {
                for (j = 0; j < countOpt - 1; j++) {
                    opts = select.options;
                    rez1 = reg.exec(opts[j].innerHTML);
                    rez2 = reg.exec(opts[j + 1].innerHTML);
                    if (rez1 && rez2) {
                        rez1 = +rez1[id + 1];
                        rez2 = +rez2[id + 1];
                        if (rez1 > rez2) {
                            buff = select.removeChild(opts[j + 1]);
                            select.insertBefore(buff, opts[j]);
                        }
                    }
                }
            }

            // выбираем первого противника в списке
            select.options[0].selected = true;
        }; // 2}}}

        /**
         * @method setSortListEnemy {{{2
         */
        this.setSortListEnemy = function () {
            var walk = general.$('walk'),
                target = general.$('euids').parentNode.parentNode.parentNode,
                tr;

            // если есть чекбокс "Подойти ближе"
            if (walk) {
                walk.parentNode.insertBefore(general.doc.createElement('br'),
                        walk);
            } else {
                tr = general.doc.createElement('tr');
                tr.innerHTML = '<td colspan="2" style="padding-bottom:5px;">' +
                    '<br></td>';
                target.parentNode.insertBefore(tr, target);
            }

            var spanSortButtons = general.doc.createElement('span');
            spanSortButtons.setAttribute('style', 'font-size: 8pt; ' +
                    'margin-left: 20px;');
            spanSortButtons.innerHTML = '<span id="s0">[номер]</span> ' +
                '<span id="s1">[лвл]</span> <span id="s2">[дальность]</span> ' +
                '<span id="s3">[видимость]</span> <span id="s4">[HP ост.]' +
                '</span> <span id="s5">[HP всего]</span>';

            target = walk ? walk.parentNode : tr.firstElementChild;
            target.appendChild(spanSortButtons);

            var button, i;
            for (i = 0; i < 6; i++) {
                button = general.$('s' + i);
                button.addEventListener('click', this.sortEnemyList, false);
            }

            general.$('s' + (general.getData(4)[2] || '0')).click();
        }; // 2}}}

        /**
         * @method clickElem {{{2
         * @param   {HTMLElement}   elem
         */
        this.clickElem = function (elem) {
            var _elem = elem;
            if (_elem) {
                _elem.click();
            }
        }; // 2}}}

        /*
         * @method setWalk {{{2
         * @param   {int}   ind
         */
        this.setWalk = function (ind) {
            var dataSt = general.getData(4),
                walk = general.$('walk');

            if (walk) {
                // noinspection JSRemoveUnnecessaryParentheses
                if ((!walk.checked && dataSt[ind]) ||
                        (walk.checked && !dataSt[ind])) {
                    walk.click();
                }
            }
        }; // 2}}}

        /**
         * @method setStroke {{{2
         */
        this.setStroke = function () {
            var dataSt = general.getData(4);

            // Дуэли - отходить можно только в центр (чекбоксы лево и
            // право не активны). Стрелям тоже всегда в центр.
            if (general.doc.querySelector('#defence1:disabled')) {
                this.clickElem(general.$('defence2'));
                this.clickElem(general.$('left_attack2'));
                this.clickElem(general.$('right_attack2'));

                return;
            }

            // если в хранилище есть запись в кого стреляли
            // (сказали ход), то устанавливаем именно его
            if (dataSt[11]) {
                var options = general.$('euids').querySelectorAll('option'),
                    i;

                for (i = 0; i < options.length; i++) {
                    if (new RegExp('^' + dataSt[11] + '\\.').
                            test(options[i].innerHTML)) {
                        options[i].selected = true;
                        break;
                    }
                }

                // левая рука
                if (dataSt[12]) {
                    this.clickElem(general.$('left_attack' + dataSt[12]));
                }

                // правая рука
                if (dataSt[13]) {
                    this.clickElem(general.$('right_attack' + dataSt[13]));
                }

                // куда отходим
                if (dataSt[14]) {
                    this.clickElem(general.$('defence' + dataSt[14]));
                }

                // если грена
                if (dataSt[15]) {
                    this.clickElem(general.$('bagaboom'));
                }

                // подходим или нет
                this.setWalk(16);

                // общий навык
                if (dataSt[19]) {
                    this.clickElem(general.$('apmid'));
                }

                // навык специалиста
                if (dataSt[20]) {
                    this.clickElem(general.$('apsid'));
                }

                // подствол
                if (dataSt[24]) {
                    this.clickElem(general.$('sbw'));
                }

                this.clearSavedStrokeAfterSay();

                return;
            }

            // отмечен чекбокс "запомнить ход"
            if (dataSt[3]) {
                // левая рука
                if (dataSt[5]) {
                    this.clickElem(general.$('left_attack' + dataSt[5]));
                }

                // правая рука
                if (dataSt[6]) {
                    this.clickElem(general.$('right_attack' + dataSt[6]));
                }

                // куда отходим
                if (dataSt[7]) {
                    this.clickElem(general.$('defence' + dataSt[7]));
                }

                // граната
                if (dataSt[8] && general.$('bagaboom')) {
                    this.clickElem(general.$('bagaboom'));
                }

                // подствол
                if (dataSt[24]) {
                    this.clickElem(general.$('sbw'));
                }

                // подходим или нет
                this.setWalk(9);

            // отмечен чебокс "не дублировать цель" и две руки
            } else if (dataSt[4] && !general.$('span_two_hand').style.display) {
                var rightAttack = general.doc.querySelector('input' +
                        '[type="radio"][name^="right_attack"]:checked'),
                    leftAttack = general.doc.querySelector('input' +
                        '[type="radio"][name^="left_attack"]:checked'),
                    x = /\d/.exec(rightAttack.id)[0],
                    y = /\d/.exec(leftAttack.id)[0];

                // если ход дублируется
                if (x === y) {
                    while (x === y) {
                        x = this.getRandom1to3();
                        y = this.getRandom1to3();
                    }

                    this.clickElem(general.$('right_attack' + x));
                    this.clickElem(general.$('left_attack' + y));
                }
            }
        }; // 2}}}

        /**
         * @method setHandlerSubmit {{{2
         */
        this.setHandlerSubmit = function () {
            var s = general.doc.createElement('script');
            s.innerHTML = 'function fight_mod() {' +
                    'var dataStAll = localStorage.getItem(\'' +
                            general.STORAGENAME + '\').split(\'@\'),' +
                        'dataSt = dataStAll[4].split(\'|\'),' +
                    'elem;' +

                    'dataSt[5] = \'\';' +   // левая
                    'dataSt[6] = \'\';' +   // правая
                    'dataSt[7] = \'\';' +   // куда отходим
                    'dataSt[8] = \'\';' +   // граната
                    'dataSt[9] = \'\';' +   // подходим или нет

                    // левая рука
                    'if (elem = document.' +
                            // eslint-disable-next-line no-useless-escape
                            'querySelector(\'input[type=\"radio\"]' +
                                // eslint-disable-next-line no-useless-escape
                                '[id^=\"left_attack\"]:checked\')) {' +
                        'dataSt[5] = /left_attack(\\d)/.exec(elem.id)[1];' +
                    '}' +

                    // правая рука
                    'if (elem = document.' +
                            // eslint-disable-next-line no-useless-escape
                            'querySelector(\'input[type=\"radio\"]' +
                                // eslint-disable-next-line no-useless-escape
                                '[id^=\"right_attack\"]:checked\')) {' +
                        'dataSt[6] = /right_attack(\\d)/.exec(elem.id)[1];' +
                    '}' +

                    // куда отходим
                    'if (elem = document.' +
                            // eslint-disable-next-line no-useless-escape
                            'querySelector(\'input[type=\"radio\"]' +
                                // eslint-disable-next-line no-useless-escape
                                '[id^=\"defence\"]:checked\')) {' +
                        'dataSt[7] = /defence(\\d)/.exec(elem.id)[1];' +
                    '}' +

                    // граната
                    'if (elem = document.' +
                            'querySelector(\'#bagaboom:checked\')) {' +
                        'dataSt[8] = \'1\';' +
                    '}' +

                    // подходим или нет
                    'if (elem = document.querySelector(\'#walk:checked\')) {' +
                        'dataSt[9] = \'1\';' +
                    '}' +

                    'dataStAll[4] = dataSt.join(\'|\');' +
                    'localStorage.setItem(\'' + general.STORAGENAME +
                        '\', dataStAll.join(\'@\'));' +
                    'fight();' +
                '}';

            general.doc.body.appendChild(s);
        }; // 2}}}

        /**
         * @method setControlOfShooting {{{2
         */
        this.setControlOfShooting = function () {
            var divGenerator = general.doc.createElement('div'),
                bf = general.$('bf'),
                coord = new GetPos().init(bf);

            divGenerator.setAttribute('style', 'position: absolute; top: ' +
                    coord.y + 'px; left: ' + coord.x + 'px; ' +
                    'margin: 5px 0 0 5px;');

            // если две руки, то "не дублировать цель" делаем видимым
            var vis = general.$('left_attack1') && general.$('right_attack1') ?
                        '' : 'none';

            // если одна рука, то сбрасываем чекбоксы
            // "Говорить только левую руку" и "Говорить только правую руку"
            if (vis) {
                var stData = general.getData(4);
                stData[18] = '';
                stData[23] = '';
                general.setData(stData, 4);
            }

            divGenerator.innerHTML = '<input type="checkbox" ' +
                'id="save_stroke">  <label for="save_stroke" ' +
                'style="vertical-align: top;">запомнить ход</label><br>' +
                '<span id="span_two_hand" style="display: ' + vis +
                ';"><input type="checkbox" id="repeat_two_hand"> <label ' +
                'for="repeat_two_hand" style="vertical-align: top;">не ' +
                'дублировать цель</label></span>';
            bf.appendChild(divGenerator);

            var chkRememberStroke = general.$('save_stroke'),
                chkNoDuplicateTarget = general.$('repeat_two_hand'),
                goButton = general.doc.
                    querySelector('a[href^="javascript:void(fight"]');

            var _this = this;
            chkRememberStroke.addEventListener('click', function () {
                var dataSt = general.getData(4),
                    thischk = this;

                if (thischk.checked) {
                    chkNoDuplicateTarget.checked = false;
                    dataSt[4] = '';

                    goButton.setAttribute('href',
                            ['javascript', ':', 'void(fight_mod())'].join(''));
                    dataSt[3] = '1';
                    general.setData(dataSt, 4);
                    _this.setStroke();
                } else {
                    goButton.setAttribute('href',
                            ['javascript', ':', 'void(fight())'].join(''));
                    dataSt[3] = '';
                    general.setData(dataSt, 4);
                }
            }, false);

            chkNoDuplicateTarget.addEventListener('click', function () {
                var dataSt = general.getData(4),
                    thischk = this;

                if (thischk.checked) {
                    chkRememberStroke.checked = false;
                    dataSt[3] = '';
                }

                dataSt[4] = thischk.checked ? '1' : '';
                general.setData(dataSt, 4);
                _this.setStroke();
            }, false);

            // установим свой обработчик нажатия кнопки "Сделать ход"
            // (если установлен чекбокс "запомнить ход", то отправленный
            // ход будет запоминаться в localStorage)
            this.setHandlerSubmit();

            var dataSt = general.getData(4);
            if (dataSt[3]) {                    // чекбокс "запомнить ход"
                chkRememberStroke.click();
            } else if (dataSt[4]) {             // чекбокс "не дублировать цель"
                chkNoDuplicateTarget.click();
            } else {                            // все чекбоксы сброшены
                this.setStroke();
            }
        }; // 2}}}

        /**
         * @method changeSelectEnemies {{{2
         */
        this.changeSelectEnemies = function () {
            var select = general.$('euids'),
                span = general.$('spanCheckRange'),
                i;

            for (i = 0; i < select.options.length; i++) {
                if (select.options[i].selected) {
                    span.setAttribute('style', /!/.
                        test(select.options[i].innerHTML) ?
                                'color: #FF0000;' : '');
                    break;
                }
            }
        }; // 2}}}

        /**
         * @method clickImageFighters {{{2
         * @param   {Object}   opt
         * @param   {Object}    _this
         * @return  {Function}
         */
        this.clickImageFighters = function (opt, _this) {
            return function () {
                opt.selected = true;
                _this.changeSelectEnemies();
            };
        }; // 2}}}

        /**
         * @method showTooltip {{{2
         * @param   {String}    ttl
         * @param   {Object}    _this
         * @return  {Function}
         */
        this.showTooltip = function (ttl, _this) {
            return function () {
                var getPos = new GetPos(),
                    obj;

                // относительно чего будем выравнивать тултип
                if (general.viewMode) {
                    obj = {
                        x: general.doc.querySelector('table[background$=' +
                            '"/battleField.gif"]').nextElementSibling.
                                nextElementSibling,
                        y: 14
                    };
                } else {
                    obj = {x: _this.inpTextChat, y: 20};
                }

                _this.tooltip.innerHTML = ttl;
                _this.tooltip.style.top = String(getPos.init(obj.x).y - obj.y);
                _this.tooltip.style.left = String(getPos.init(this).x - 50);
                _this.tooltip.style.display = '';
            };
        }; // 2}}}

        /**
         * @method hideTooltip {{{2
         * @param   {Object}    _this
         * @return  {Function}
         */
        this.hideTooltip = function (_this) {
            return function () {
                _this.tooltip.style.display = 'none';
            };
        }; // 2}}}

        /**
         * @method setTooltipsFighters {{{2
         * @param   {HTMLTableElement}  table
         */
        this.setTooltipsFighters = function (table) {
            var selectEnemy = general.$('euids'),
                _this = this;
            // помещаем "Противник:" (слева от селекта) в span
            // если не достаем до выбранного противника,
            // то эта надпись будет красной
            if (selectEnemy) {
                selectEnemy.parentNode.removeChild(selectEnemy.previousSibling);
                var spanCheckRange = general.doc.createElement('span');
                spanCheckRange.setAttribute('id', 'spanCheckRange');
                spanCheckRange.innerHTML = 'Противник: ';
                selectEnemy.parentNode.
                    insertBefore(spanCheckRange, selectEnemy);
                selectEnemy.addEventListener('change', function () {
                    _this.changeSelectEnemies();
                }, false);
                this.changeSelectEnemies();
            }

            var options = selectEnemy ?
                    selectEnemy.querySelectorAll('option') : false,
                img = table.querySelectorAll('img'),
                txtOptions,
                ttlName,
                visib,
                pers,
                name,
                ttl,
                i,
                j;

            for (i = 0; i < img.length; i++) {
                ttl = img[i].getAttribute('title');
                if (!ttl || !/(.*) \[\d+/.test(ttl)) {
                    continue;
                }

                ttlName = /(.*) \[\d+/.exec(ttl)[1].replace(/&amp;/, '&');
                // если есть список выбора врага (ход не сделан)
                if (options) {
                    // noinspection JSUnresolvedVariable
                    for (j = 0; j < options.length; j++) {
                        txtOptions = options[j].innerHTML.replace(/&amp;/, '&');
                        if (txtOptions.indexOf(ttlName) !== -1) {
                            // кликаем по картинке, выбираем цель
                            img[i].setAttribute('style', 'cursor: pointer;');
                            img[i].addEventListener('click',
                                    this.clickImageFighters(options[j], this),
                                        false);
                            break;
                        }
                    }
                }

                for (name in this.allFighters) {
                    if (this.allFighters.hasOwnProperty(name) &&
                            name === ttlName) {
                        pers = this.allFighters[name];
                        ttl = '<span style="font-weight: bold;">' +
                            (general.viewMode ?
                                    (pers.num ? pers.num + '. ' : '') :
                                        this.enemies[name] ?
                                            this.enemies[name] + '. ' : '') +
                            '<span style="color: #0000FF;">' + pers.lvl +
                            '</span> ' +
                            '<span' +
                            (pers.npc ? ' style="opacity:0.6;"' : '') + '>' +
                            name + '</span> ' +
                            '<span>[' + pers.hp[1] + '/' + pers.hp[2] + ']' +
                            '</span>' +
                            '</span>' +
                            '<div style="color: #B85006; ' +
                            'margin-left: 10px;">Видимость: ' + pers.visib +
                            pers.skill + '</div>' +
                            '<div>' + pers.allWeapon + '</div>';

                        // прозрачность перса в зависимости от его видимости
                        visib = +/\d+/.exec(pers.visib)[0];
                        visib = (visib / 100).toFixed(1);
                        if (visib < 0.3) {
                            visib = 0.3;
                        }

                        img[i].style.opacity = visib.toString();
                        img[i].style.marginLeft = '6px';
                        break;
                    }
                }

                // показываем тултип
                img[i].addEventListener('mouseover',
                    this.showTooltip(ttl, this), false);
                // скрываем тултип
                img[i].addEventListener('mouseout',
                        this.hideTooltip(this), false);
                // по двойному клику вставляем ник персонажа в поле ввода чата
                img[i].addEventListener('dblclick',
                    _this.setPersNikInChat(_this, ttlName), false);

                // удаляем оригинальный title
                img[i].removeAttribute('title');
            }
        }; // 2}}}

        /**
         * @method isEven {{{2
         * @param   {int}   x
         */
        this.isEven = function (x) {
            return x % 2 === 0;
        }; // 2}}}

        /**
         * @method changeLocationFighters {{{2
         */
        this.changeLocationFighters = function () {
            var table;
            // в бою
            if (!general.viewMode) {
                var bf = general.$('bf');
                // ход сделан, вставляем сохраненную таблицу
                if (/Ждём ход противника/i.test(bf.innerHTML)) {
                    if (this.graphTable) {
                        var target = bf.querySelector('a').parentNode;
                        target.appendChild(general.doc.createElement('br'));
                        target.appendChild(general.doc.createElement('br'));
                        target.appendChild(this.graphTable);
                        target.appendChild(general.doc.createElement('br'));
                        this.setTooltipsFighters(this.graphTable);
                        return;
                    }
                } else {    // ход еще не сделан
                    table = bf.querySelector('div>table:last-child');
                }
            } else {    // режим наблюдения за боем
                table = this.leftRightCommands[0].nextElementSibling.
                    lastElementChild.previousElementSibling;

                if (/Наблюдатели/.test(table.innerHTML)) {
                    table = table.previousElementSibling;
                }
            }

            table.setAttribute('style', 'border-collapse: collapse;');
            table.setAttribute('background', this.imgPath + 'battleField.gif');

            // вставим пустую строку после таблицы
            if (!general.viewMode) {    // в бою
                table.parentNode.appendChild(general.doc.createElement('br'));
            } else {    // режим наблюдения за боем
                table.parentNode.insertBefore(general.doc.createElement('br'),
                    table.nextElementSibling);
            }

            var reg = /\/(left|right)_.*\.gif/,
                td = table.querySelectorAll('td'),
                prBarWidth = 27,
                rightDC = -1,
                leftDC = -1,
                divBattleField,
                diffCommand,
                pTitleName,
                trNumbers,
                divHealth,
                tdNumber,
                cloneTd,
                myInd,
                title,
                hpClr,
                even,
                divL,
                divR,
                flag,
                img,
                DC,
                hp,
                i,
                j;

            for (i = 0; i < td.length; i++) {
                td[i].setAttribute('style', 'border: 1px dotted #FFFFFF; ' +
                        'vertical-align: center;');
                // если в "TD" нет картинки перса
                if (!td[i].querySelector('img').getAttribute('title')) {
                    continue;
                }

                cloneTd = td[i].cloneNode(true);
                td[i].innerHTML = '';
                img = cloneTd.querySelectorAll('img');

                // узнаем есть ли в ячейке бойцы из разных команд
                diffCommand = false;
                for (j = 0; j < img.length - 1; j++) {
                    // берем из атрибута srs 'left' или 'right'
                    if (reg.exec(img[j].src)[1] !==
                            reg.exec(img[j + 1].src)[1]) {
                        diffCommand = true;
                        break;
                    }
                }

                flag = false;
                for (j = 0; j < img.length; j++) {
                    title = img[j].getAttribute('title');
                    pTitleName = /^(.*)\s\[\d+/.exec(title);

                    // ячейка где находится мой перс
                    if (!general.viewMode && pTitleName &&
                            pTitleName[1] === this.myPers.name) {
                        myInd = -1 * i;
                    }

                    // крайний левый и крайний правый персонаж от центра
                    if (reg.exec(img[j].src)[1] === 'left') {
                        leftDC = i;
                    } else if (rightDC === -1) {
                        rightDC = i;
                    }

                    divBattleField = general.doc.createElement('div');

                    // формируем прогресс бар здоровья
                    hp = /\[(\d+)\/(\d+)\]/.exec(title);
                    if (hp) {
                        divHealth = general.doc.createElement('div');
                        divHealth.setAttribute('style',
                            'width: ' + prBarWidth + 'px; ' +
                            'background-color: #FFEFDD; ' +
                            'margin: 7px 1px 3px 1px;');

                        // вычисляем процент оставшегося здоровья
                        hp = Math.ceil(+hp[1] * 100 / +hp[2]);
                        hpClr = hp < 30 ? '#FF0000' :
                                    hp < 80 ? '#C44A00' : '#339933';

                        divHealth.innerHTML = '<div style="' +
                            'height: 2px; width: ' +
                                Math.ceil(prBarWidth / 100 * hp) + 'px; ' +
                            'background-color: ' + hpClr + ';"></div>';
                        divBattleField.appendChild(divHealth);
                    }

                    if (!diffCommand) {
                        td[i].appendChild(divBattleField);
                        td[i].lastElementChild.
                                appendChild(img[j].cloneNode(true));
                    } else {
                        if (!flag) {
                            divL = general.doc.createElement('div');
                            divL.setAttribute('style',
                                    'display: inline-block;');
                            td[i].appendChild(divL);
                            divR = general.doc.createElement('div');
                            divR.setAttribute('style',
                                    'display: inline-block;');
                            td[i].appendChild(divR);
                            flag = true;
                        }

                        divBattleField.appendChild(img[j].cloneNode(true));
                        if (reg.exec(img[j].src)[1] === 'left') {
                            td[i].firstElementChild.appendChild(divBattleField);
                        } else {
                            td[i].lastElementChild.appendChild(divBattleField);
                        }
                    }
                }
            }

            DC = Math.abs(leftDC - rightDC);
            even = this.isEven(DC);
            DC = even ? leftDC + DC / 2 : leftDC + Math.floor(DC / 2);

            // если нет изображения моего перса на поле боя (бывает такой глюк)
            // будем отсчитывать от динамического центра
            if (isNaN(myInd)) {
                myInd = -1 * DC;
            }

            // расставляем дальность и ДЦ
            trNumbers = general.doc.createElement('tr');
            table.firstElementChild.insertBefore(trNumbers,
                    table.firstElementChild.firstElementChild);

            for (i = 0; i < td.length; i++) {
                tdNumber = general.doc.createElement('td');
                trNumbers.appendChild(tdNumber);

                tdNumber.innerHTML = String(Math.abs(myInd));
                // если индекс нулевой (там где я стою) то цвет синий
                if (myInd) {
                    tdNumber.setAttribute('style',
                        'text-align: center; font-size: 7pt; border: 1px ' +
                        'dotted #FFFFFF;');
                } else {
                    tdNumber.setAttribute('style', 'text-align: center; ' +
                        'font-size: 8pt; color :#0000FF; font-weight: bold; ' +
                        'border: 1px dotted #FFFFFF;');
                }

                myInd++;

                if (i !== DC) {
                    continue;
                }

                tdNumber.setAttribute('style', 'text-align: center; ' +
                    'font-size: 8pt; color: #FF0000; font-weight: bold; ' +
                    'border: 1px dotted #FFFFFF;');

                if (!even) {
                    DC++;
                    even = true;
                }
            }

            if (!general.viewMode) {
                this.graphTable = table.cloneNode(true);
            }

            this.setTooltipsFighters(table);
        }; // 2}}}

        /**
         * @method setCountStroke {{{2
         * @param   {object}    obj
         */
        this.setCountStroke = function (obj) {
            // персы, которые сделали ход (зеленые)
            var greenPersLinks = obj.
                    querySelectorAll('a[href*="/info.php?id="]' +
                            '[style*="#008800"]');

            if (greenPersLinks.length) {
                var persLinkInBattle, greenPersId, i;
                for (i = 0; i < greenPersLinks.length; i++) {
                    greenPersId = /\/info\.php\?id=(\d+)/.
                        exec(greenPersLinks[i].href)[1];
                    persLinkInBattle = general.doc.
                        querySelector('a[href$="/info.php?id=' + greenPersId +
                            '"]');

                    if (persLinkInBattle) {
                        persLinkInBattle.style.color = '#008800';
                    }
                }

                this.setMyInfo(greenPersLinks.length);
            }
        }; // 2}}}

        /**
         * @method setColorFighters {{{2
         * @param   {Object}    _this
         * @return  {Function}
         */
        this.setColorFighters = function (_this) {
            return function () {
                // ход сделан
                if (/Ждём ход противника/i.test(general.$('bf').innerHTML)) {
                    // ссылка на страницу НЕ JS-версии боя
                    var url = general.loc.replace('btl.php', 'b.php'),
                        ajax = new AjaxQuery();

                    ajax.init(url, 'GET', null, true, function (xhr) {
                        var span = general.doc.createElement('span');
                        span.innerHTML = xhr.responseText;
                        _this.setCountStroke(span);
                    }, function () {
                        general.root.console.log('Error XHR to: ' + url);
                    });
                }
            };
        }; // 2}}}

        /**
         * @method start {{{2
         */
        this.start = function () {
            // сразу скрываем тултип (на всякий случай, если остался)
            this.tooltip.style.display = 'none';

            var selectEnemies = general.$('euids'),
                dataSt = general.getData(4),
                options,
                i;

            // в бою
            // noinspection JSUnresolvedVariable
            if (!general.viewMode) {
                // если есть список выбора врага (ход не сделан)
                if (selectEnemies) {
                    // играем звук о начале хода
                    if (!this.checkSound) {
                        new PlaySound().init(dataSt[22]);
                        this.checkSound = true;
                    }

                    var tmp;
                    // обнуляем хэш из выпадающего списка врагов (имя --> номер)
                    this.enemies = {};
                    options = selectEnemies.querySelectorAll('option');
                    for (i = 0; i < options.length; i++) {
                        tmp = /^(\d+)\. (.+)\[\d+\]/.exec(options[i].innerHTML);
                        if (tmp) {
                            this.enemies[tmp[2].replace(/&amp;/, '&')] = tmp[1];
                        }
                    }
                } else {    // ход сделан
                    this.checkSound = false;
                    if (!this.enemies) {
                        return;
                    }
                }
            } else {    // в режиме наблюдения за боем
                dataSt[17] = '';    // удаляем данные из списка врагов
                general.setData(dataSt, 4);
            }

            this.getLeftRightCommands();

            // ссылки на персов слева и справа
            this.leftPers = this.getPers(this.leftRightCommands[0]);
            this.rightPers = this.getPers(this.leftRightCommands[1]);

            // бой закончился, режим наблюдения за боем,
            // форма для отправки сообщений присутствует
            if (!this.leftPers.length || !this.rightPers.length) {
                return;
            }

            // расстановка конвертиков и сбор дополнительной
            // информации (если они еще не были установлены)
            if (this.leftPers[0] &&
                    !this.leftPers[0].parentNode.
                        querySelector('span[name="sendmessenv"]')) {
                this.allFighters = {};
                this.setEnvelope();
            }

            // в бою
            if (!general.viewMode) {
                // в бою установим свои данные вверху
                this.setMyInfo(0);

                // расширяем данные в списке выбора
                var enemyName, tmpObj;
                if (selectEnemies) {
                    for (i = 0; i < options.length; i++) {
                        // eslint-disable-next-line no-useless-escape
                        enemyName = /^\d+\. ([^\[]+)\[/.
                            exec(options[i].innerHTML);

                        if (!enemyName) {
                            continue;
                        }

                        enemyName = enemyName[1].replace(/&amp;/, '&');
                        tmpObj = this.allFighters[enemyName];

                        if (!tmpObj) {
                            continue;
                        }

                        options[i].innerHTML = this.enemies[enemyName] + '. ' +
                            tmpObj.lvl + ' - ' + tmpObj.dist +
                            // если до цели не достаем, ставим '!'
                            (/#ffe0e0/.test(options[i].getAttribute('style')) ?
                                    '!' : '') +
                            ' (' + tmpObj.visib + ') [' + tmpObj.hp[1] +
                            ' / ' + tmpObj.hp[2] +  '] ' + enemyName +
                            ': ' + tmpObj.weapon + ' &nbsp;';
                    }

                    // сортируем список выбора
                    this.setSortListEnemy();

                    // установка чекбоксов "запомнить ход" и
                    // "не дублировать цель"
                    this.setControlOfShooting();

                    // показываем кнопку "Сказать ход"
                    this.sayMoveButton.style.display = '';
                } else {    //уже сходили
                    // прячем кнопку "Сказать ход"
                    this.sayMoveButton.style.display = 'none';
                }

                // обновление данных в бою
                var refreshBattle = general.getData(4)[0];
                if (refreshBattle && !this.tmRefreshBattle) {
                    this.tmRefreshBattle = general.root.
                        setInterval(function () {
                            var updLink = general.doc.
                                    querySelector('a[href*="updatedata()"], ' +
                                        'input[onclick$="void(updatedata())"]');

                            if (updLink) {
                                updLink.click();
                            }
                        }, +refreshBattle * 1000);
                }

                // подсвечиваем персонажей, которые уже сделали ход,
                // устанавливаем количество персонажей, сделавших ход
                if (!this.tmHighlightPers) {
                    this.setColorFighters(this);
                    this.tmHighlightPers = general.root.
                        setInterval(this.setColorFighters(this), 3000);
                }
            }

            // изменяем расположение бойцов, ставим тултипы...
            this.changeLocationFighters();
        }; // 2}}}

        /**
         * @method setChatInterface {{{2
         */
        this.setChatInterface = function () {
            // чекбокс "Сказать своей команде"
            var sayOnlyMyCommand = general.doc.createElement('input');
            sayOnlyMyCommand.type = 'checkbox';
            sayOnlyMyCommand.setAttribute('title', 'Сказать своей команде');
            this.inpTextChat.parentNode.insertBefore(sayOnlyMyCommand,
                    this.inpTextChat);

            // чекбокс "Сказать как координатор"
            var sayAsCoord = general.doc.createElement('input');
            sayAsCoord.type = 'checkbox';
            sayAsCoord.setAttribute('style', 'margin-right: 10px;');
            sayAsCoord.setAttribute('title', 'Сказать как координатор');
            this.inpTextChat.parentNode.insertBefore(sayAsCoord,
                    this.inpTextChat);

            var stData = general.getData(4);
            if (!stData[25]) {
                sayAsCoord.style.display = 'none';
                sayOnlyMyCommand.setAttribute('style', 'margin-right: 10px;');
            }

            if (general.viewMode) {
                sayOnlyMyCommand.style.display = 'none';
            }

            var _this = this;
            sayOnlyMyCommand.addEventListener('click', function () {
                var dataSt = general.getData(4),
                    thisChk = this;

                if (thisChk.checked) {
                    if (sayAsCoord.checked) {
                        dataSt[26] = '';
                        sayAsCoord.checked = false;
                        _this.inpTextChat.value = _this.inpTextChat.value.
                            replace(/^\s*!\*\s*/, '');
                    }

                    dataSt[10] = '1';
                    var chatMessage = _this.inpTextChat.value;
                    // noinspection RegExpSingleCharAlternation
                    if (!general.viewMode &&
                            !/^\s*(~|\*|@)/.test(chatMessage)) {
                        _this.inpTextChat.value = '~' + chatMessage;
                    }

                    if (_this.intervalUpdateInpTextChat) {
                        general.root.clearInterval(_this.
                            intervalUpdateInpTextChat);
                    }

                    // костыль после отправки сообщения в чат
                    if (!general.viewMode) {
                        _this.intervalUpdateInpTextChat = general.root.
                            setInterval(function () {
                                if (!_this.inpTextChat.value) {
                                    _this.inpTextChat.value = '~';
                                }
                            }, 1000);
                    }
                } else {
                    dataSt[10] = '';
                    // noinspection RegExpSingleCharAlternation
                    _this.inpTextChat.value = _this.inpTextChat.value.
                        replace(/^\s*(~|\*|@)\s*/, '');

                    if (_this.intervalUpdateInpTextChat) {
                        general.root.clearInterval(_this.
                            intervalUpdateInpTextChat);
                    }
                }

                general.setData(dataSt, 4);
                _this.inpTextChat.focus();
            }, false);

            sayAsCoord.addEventListener('click', function () {
                var dataSt = general.getData(4),
                    thisChk = this;

                if (thisChk.checked) {
                    if (sayOnlyMyCommand.checked) {
                        dataSt[10] = '';
                        sayOnlyMyCommand.checked = false;
                        _this.inpTextChat.value = _this.inpTextChat.value.
                            replace(/^\s*~\s*/, '');
                    }

                    dataSt[26] = '1';
                    var chatMessage = _this.inpTextChat.value;
                    // noinspection RegExpSingleCharAlternation
                    if (!/^!\*/.test(chatMessage)) {
                        _this.inpTextChat.value = '!*' + chatMessage;
                    }

                    if (_this.intervalUpdateInpTextChat) {
                        general.root.clearInterval(_this.
                            intervalUpdateInpTextChat);
                    }

                    // костыль после отправки сообщения в чат
                    _this.intervalUpdateInpTextChat = general.root.
                        setInterval(function () {
                            if (!_this.inpTextChat.value) {
                                _this.inpTextChat.value = '!*';
                            }
                        }, 1000);
                } else {
                    dataSt[26] = '';
                    // noinspection RegExpSingleCharAlternation
                    _this.inpTextChat.value = _this.inpTextChat.value.
                        replace(/^\s*!\*\s*/, '');

                    if (_this.intervalUpdateInpTextChat) {
                        general.root.clearInterval(_this.
                            intervalUpdateInpTextChat);
                    }
                }

                general.setData(dataSt, 4);
                _this.inpTextChat.focus();
            }, false);

            if (stData[10]) {
                sayOnlyMyCommand.click();
            } else if (stData[26]) {
                sayAsCoord.click();
            }

            if (general.viewMode) {
                return;
            }

            // если отмечен чекбокс "Сказать своей команде", символ '~' стереть
            // будет нельзя
            // если отмечен чекбокс "Сказать как координатор", символы '!*'
            // стереть будет нельзя
            this.inpTextChat.addEventListener('input', function (e) {
                var thisInp = this;
                if (sayOnlyMyCommand.checked && !thisInp.value) {
                    thisInp.value = '~';
                }

                if (sayAsCoord.checked &&
                        (!thisInp.value || thisInp.value === '!')) {
                    thisInp.value = '!*';
                }

                // при нажатии <Enter> сохраняем установленный ход
                var ev = e || general.root.event,
                    key = ev.keyCode;

                if (key === 13 || key === 10) {
                    _this.sayMove(_this, true);
                }
            }, false);

            // при клике на "Написать" сохраняем установленный ход
            var writeButton = general.doc.querySelector('input[type="submit"]' +
                '[value="Написать"]');
            writeButton.addEventListener('click', function () {
                _this.sayMove(_this, true);
            }, false);

            // кнопа "Сказать ход"
            this.sayMoveButton = general.doc.createElement('input');
            this.sayMoveButton.type = 'button';
            this.sayMoveButton.setAttribute('class', 'mainbutton');
            this.sayMoveButton.setAttribute('style', 'background: #F5FFF5; ' +
                'border-radius: 3px; cursor: pointer; box-shadow: ' +
                '1px 1px 2px rgba(122,122,122,0.5);');
            this.sayMoveButton.value = 'Сказать ход';
            this.sayMoveButton.addEventListener('click', function () {
                _this.sayMove(_this, false);
            }, false);
            sayOnlyMyCommand.parentNode.insertBefore(this.sayMoveButton,
                    sayOnlyMyCommand);

            // если двурукий, устанавливаем чекбоксы:
            // "Говорить только правую руку" и "Говорить только левую руку"
            if (general.$('left_attack1') && general.$('right_attack1')) {
                // "Говорить только левую руку"
                var sayOnlyLeftHand = general.doc.createElement('input');
                sayOnlyLeftHand.setAttribute('id', 'sayOnlyLeftHand');
                sayOnlyLeftHand.type = 'checkbox';
                sayOnlyLeftHand.setAttribute('title',
                    'Говорить только левую руку');
                sayOnlyLeftHand.checked = !!stData[18];
                sayOnlyMyCommand.parentNode.
                    insertBefore(sayOnlyLeftHand, sayOnlyMyCommand);

                sayOnlyLeftHand.addEventListener('click', function () {
                    var data = general.getData(4),
                        this_checked = sayOnlyLeftHand.checked;

                    data[18] = this_checked ? '1' : '';
                    general.setData(data, 4);

                    // если отмечен, сбрасываем чекбокс для правой руки
                    var onlyRightHand = general.$('sayOnlyRightHand');
                    if (this_checked && onlyRightHand.checked) {
                        onlyRightHand.click();
                    }
                }, false);

                // "Говорить только правую руку"
                var sayOnlyRightHand = general.doc.createElement('input');
                sayOnlyRightHand.setAttribute('id', 'sayOnlyRightHand');
                sayOnlyRightHand.type = 'checkbox';
                sayOnlyRightHand.setAttribute('title',
                    'Говорить только правую руку');
                sayOnlyRightHand.checked = !!stData[23];
                sayOnlyMyCommand.parentNode.
                    insertBefore(sayOnlyRightHand, sayOnlyLeftHand);

                sayOnlyRightHand.addEventListener('click', function () {
                    var data = general.getData(4),
                        this_checked = sayOnlyRightHand.checked;

                    data[23] =  this_checked ? '1' : '';
                    general.setData(data, 4);

                    // если отмечен, сбрасываем чекбокс для левой руки
                    var onlyLeftHand = general.$('sayOnlyLeftHand');
                    if (this_checked && onlyLeftHand.checked) {
                        onlyLeftHand.click();
                    }
                }, false);
            }

            // добавляем кнопку "Обновить"
            var buttonUpdate = general.doc.createElement('input');
            buttonUpdate.type = 'button';
            buttonUpdate.setAttribute('class', 'mainbutton');
            buttonUpdate.setAttribute('style', 'background: #F5FFF5; ' +
                'border-radius: 3px; cursor: pointer; box-shadow: ' +
                '1px 1px 2px rgba(122,122,122,0.5);');
            buttonUpdate.value = 'Обновить';
            buttonUpdate.setAttribute('onclick',
                    ['javascript', ':', 'void(updatedata())'].join(''));

            this.inpTextChat.parentNode.appendChild(buttonUpdate);
        }; // 2}}}

        /**
         * @method changeMakebf {{{2
         */
        this.changeMakebf = function () {
            var _this = this;
            general.root.makebf = function () {
                /** @namespace general.root.waitforturn */
                /** @namespace general.root.bf1 */
                /** @namespace general.root.bf2 */
                /** @namespace general.root.bf3 */
                /** @namespace general.root.bfndl */
                general.$('bf').innerHTML = !general.root.waitforturn ?
                        general.root.bf1 : general.root.bf2 + general.root.bf3;
                general.$('bfndl').innerHTML = general.root.bfndl;
                _this.start();
            };
        }; // 2}}}

        /**
         * @method tryStart {{{2
         */
        this.tryStart = function () {
            if (general.viewMode) {
                this.setChatInterface();
                this.start();
                return;
            }

            this.inpTextChat = general.doc.querySelector('input[name="oldm"]');
            // основное поле боя
            var bf = general.$('bf');

            if (this.inpTextChat && bf &&
                    !/Загружаются данные/.test(bf.innerHTML)) {
                this.changeMakebf();
                this.setChatInterface();
                this.start();
            } else {
                // ждем загрузки фрейма с данными
                var _this = this;
                general.root.setTimeout(function () {
                    _this.tryStart();
                }, 100);
            }
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            // графическое оформление боев или НЕ JS-версия боя
            if (general.doc.querySelector('table[style*="battleground"]') ||
                    /\/b0\/b\.php/.test(general.loc) ||
                    general.root.self !== general.root.top) {
                return;
            }

            // обновление страницы, когда висим в заявке
            if (/(\/wargroup|\/warlist)\.php/.test(general.loc)) {
                var refreshAppl = general.getData(4)[1];
                if (refreshAppl && (general.doc.
                        querySelector('center>b>font[color="#990000"]') ||
                            general.doc.querySelector('td[width="70%"]>b>' +
                                    'font[color="#990000"]'))) {

                    var updateLink = general.$('updatetimer2') ||
                            general.$('updatetimer');
                    if (updateLink) {
                        general.root.setTimeout(function () {
                            general.root.location = updateLink.parentNode.href;
                        }, +refreshAppl * 1000);
                    }
                }

                return;
            }

            if (general.viewMode) {
                this.inpTextChat = general.doc.
                    querySelector('input[name="msg"]');
                // бой закончился
                if (!this.inpTextChat) {
                    return;
                }
            }

            // в бою
            if (!general.viewMode) {
                new PlaySound().init(general.getData(4)[21]);
                // ячейка для вывода информации своего перса
                var tdTop = general.doc.querySelector('td[class="txt"]' +
                        '[width="50%"][align="right"]');
                tdTop.setAttribute('width', '30%');
                tdTop.previousElementSibling.setAttribute('width', '30%');
                this.myInfoTopPanel = general.doc.createElement('td');
                this.myInfoTopPanel.setAttribute('width', '40%');
                this.myInfoTopPanel.setAttribute('style',
                        'text-align: center;');
                tdTop.parentNode.insertBefore(this.myInfoTopPanel, tdTop);
            }

            // tooltip с информацией о бойцах при наведении на них мыши
            this.tooltip = general.doc.createElement('div');
            this.tooltip.setAttribute('id', 'div_tooltip');
            this.tooltip.setAttribute('style', 'display: none; position: ' +
                'absolute; font-size: 8pt; background-color: #F5FFF5; ' +
                'padding: 3px; border: 1px solid #339933; border-radius: ' +
                '7px; box-shadow: 2px 3px 5px rgba(122,122,122,0.5);');

            general.doc.body.appendChild(this.tooltip);
            // на всякий случай, если останется виден
            this.tooltip.addEventListener('click', function () {
                var _this = this;
                _this.style.display = 'none';
            }, false);

            this.tryStart();
        }; // 2}}}
    }; // 1}}}

    /**
     * @class BlacklistHighlighting {{{1
     * @constructor
     */
    var BlacklistHighlighting = function () {
        /**
         * @property blTable
         * @type {HTMLTableElement|null}
         */
        this.blTable = null;

        /**
         * @method rememberClick {{{2
         * @param   {Object}    _this
         */
        this.rememberClick = function (_this) {
            var a = _this.blTable.querySelectorAll('a[href*="/info.php?id="]'),
                stData = general.getData(5),
                mass,
                i;

            //в ЧС никого нет
            if (!a.length) {
                stData[0] = '';
                general.setData(stData, 5);
                alert('Ваш ЧС пуст. Сначала нужно добавить персонажей.');
                return;
            }

            mass = [];
            for (i = 0; i < a.length; i++) {
                mass.push(/info\.php\?id=(\d+)/.exec(a[i].href)[1]);
            }

            stData[0] = mass.join(',');
            general.setData(stData, 5);
            _this.setHighlighting();
        }; // 2}}}

        /**
         * @method clearClick {{{2
         * @param   {Object}    _this
         */
        this.clearClick = function (_this) {
            var stData = general.getData(5);

            if (stData[0]) {
                stData[0] = '';
                general.setData(stData, 5);
                _this.setHighlighting();
            } else {
                alert('В памяти скрипта нет ЧС');
            }
        }; // 2}}}

        /**
         * @method setHighlighting {{{2
         */
        this.setHighlighting = function () {
            var a = general.doc.querySelectorAll('a[href*="/info.php?id="]'),
                stData = general.getData(5),
                link,
                id,
                i;

            for (i = 0; i < a.length; i++) {
                a[i].style.background = '';
                id = /\?id=(\d+)$/.exec(a[i].href);
                id = id && id[1].length > 3 ? id[1] : null;
                // noinspection JSUnresolvedVariable
                if (id && stData[0].indexOf(id) !== -1) {
                    a[i].style.background = '#B6B5B5';
                    // блокировка ссылки принятия боя в одиночных заявках
                    // noinspection JSUnresolvedVariable
                    if (stData[1] && /Подтверждаете бой с/.
                            test(a[i].parentNode.innerHTML)) {
                        link = general.doc.
                            querySelector('a[class="mainbutton"]' +
                                '[href*="/warlist.php?war=armed&do=5&cu="]');
                        link.removeAttribute('class');
                        link.setAttribute('style', 'text-decoration: ' +
                            'line-through; color: #808080;');
                        link.href = '#';
                    }
                }
            }
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            if (/home\.friends\.php/.test(general.loc)) {
                this.blTable = general.doc.querySelectorAll('table' +
                        '[border="0"][cellspacing="0"][cellpadding="3"]' +
                        '[class="wb"][align="center"][width="100%"]');

                if (!this.blTable[1]) {
                    return;
                }

                this.blTable = this.blTable[1];
                var buttonStyle = 'margin-left: 5px; border: solid 1px; ' +
                    'border-radius: 3px; background: #D0EED0; cursor: pointer;',
                    butRemember = general.doc.createElement('input'),
                    butClear = general.doc.createElement('input'),
                    _this = this;

                butRemember.type = 'button';
                butRemember.value = 'Запомнить ЧС';
                butRemember.setAttribute('style', buttonStyle);
                butRemember.addEventListener('click', function () {
                    _this.rememberClick(_this);
                }, false);

                butClear.title = 'Забыть';
                butClear.type = 'button';
                butClear.value = 'X';
                butClear.setAttribute('style', buttonStyle);
                butClear.addEventListener('click', function () {
                    _this.clearClick(_this);
                }, false);

                var target = this.blTable.querySelector('td');
                target.appendChild(butRemember);
                target.appendChild(butClear);
            }

            if (general.getData(5)[0]) {
                if (/gwars.*\/b0\//.test(general.loc)) {
                    general.root.setInterval(this.setHighlighting, 1000);
                } else if (/\/usertransfers\.php/.test(general.loc)) {
                    general.root.setTimeout(this.setHighlighting, 300);
                } else {
                    this.setHighlighting();
                }
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class WorkPostGrenadesBroken {{{1
     * @constructor
     */
    var WorkPostGrenadesBroken = function () {
        /**
         * @property wpgbContainer
         * @type {HTMLElement}
         */
        this.wpgbContainer = general.doc.createElement('span');
        /**
         * @property redFactory
         * @type {String}
         */
        this.redFactory = general.imgPath + 'WorkPostGrenadesBroken/' +
                'redFactory.gif';
        /**
         * @property blueFactory
         * @type {String}
         */
        this.blueFactory = general.imgPath + 'WorkPostGrenadesBroken/' +
                'blueFactory.gif';
        /**
         * @property grenades {{{2
         * @type {Array}
         */
        this.grenades = [
            // гос
            'emp_irs', 'emp_a', 'rgd5', 'grenade_f1', 'rgd2', 'lightst',
            'lights', 'rkg3', 'mdn', 'rgd2m', 'rgo', 'm84', 'rgn', 'emp_ir',
            'fg3l', 'l83a1', 'emp_s', 'm67', 'm3', 'hg78', 'hg84', 'fg6',
            'anm14', 'm34ph', 'fg7', 'fg8bd', 'r10',
            //синдовые
            'lightss', 'lightsm', 'rgd2s', 'grenade_dg1', 'fg5', 'molotov',
            'hellsbreath', 'napalm', 'ghtb', 'me85',
            //ржавка
            'old_rgd5',
            //гранатометы
            /* гос */
            'rpg', 'ptrk', 'glauncher', 'grg', 'paw20', 'rpgu', 'grom2',
            'ags30', 'gm94', 'gl06', 'gmg', 'balkan', 'rg6', 'm202', 'mm1',
            /* арт */
            'milkor', 'm32', 'mk47',
            /* пасхальные яйца */
            'pegg1', 'pegg2', 'pegg3', 'egg1', 'egg2', 'egg3'
        ]; // 2}}}

        /**
         * @method addContent {{{2
         * @param   {Array}     sms
         * @param   {Boolean}   gren
         * @param   {Boolean}   broken
         * @return  {String}
         */
        this.addContent = function (sms, gren, broken) {
            var host = ' [<a href="https://' + general.domain + '/',
                stData = general.getData(6),
                str = '';

            if (sms[0] && stData[3]) {    // письмо
                str += host + 'sms.php">' +
                    '<img src="https://images.' +
                    general.domain.replace('www.', '') +
                    '/img/letter-pc.png" title="' +
                    sms[0].getAttribute('title') +
                    '" alt="Вам письмо"></a> <span style="color: #005F00">' +
                    sms[2] + '</span>]';
            }

            if (sms[1] && stData[3]) {    // посылка
                str += host + 'items.php"><img src="https://' + general.domain +
                    '/i/woodbox.gif" title="Пришла посылка!" ' +
                    'alt="посылка"></a>]';
            }

            if (!gren && stData[5]) { // граната
                str += host + 'items.php"><span style="color: #FF0000; ' +
                    'font-weight: bold;">Грена</span></a>]';
            }

            if (broken && stData[4]) { // слом
                str += host + 'workshop.php"><span style="color: #FF0000; ' +
                    'font-weight: bold;">Слом</span></a>]';
            }

            return str;
        }; // 2}}}

        /**
         * @method startWorkPostGrenadesBroken {{{2
         * @param   {Object}    _this
         */
        this.startWorkPostGrenadesBroken = function (_this) {
            var ajaxQuery = new AjaxQuery(),
                url = 'https://' + general.domain + '/me.php';

            _this = _this || this;
            ajaxQuery.init(url, 'GET', null, true, function (xml) {
                var spanContent = general.doc.createElement('span');
                spanContent.innerHTML = xml.responseText;
                _this.wpgbContainer.innerHTML = '';
                // персонаж в пути
                if (/Вы находитесь в пути/.test(spanContent.innerHTML)) {
                    _this.wpgbContainer.innerHTML = '<span style="color: ' +
                            '#0000FF;">[в пути]</span>';
                    return;
                }

                // персонаж в бою
                if (/Идёт бой/.test(spanContent.
                        querySelector('title').innerHTML)) {
                    _this.wpgbContainer.innerHTML = '<span style="color: ' +
                        '#0000FF;">[бой]</span>';
                    return;
                }

                // проверка на новое письмо и/или посылку
                // [письмо, посылка, количество новых писем]
                var testSms = [
                    spanContent.querySelector('img[src$="/img/letter-pc.png"]'),
                    spanContent.querySelector('a>img[src$="/i/woodbox.gif"]'),
                    ''
                ];

                // если есть письмо, смотрим количество новых/не прочитанных
                if (testSms[0]) {
                    var countSmsLink = spanContent.
                            querySelector('a[href$="/sms.php"]' +
                                '[class="graybutton"]');

                    if (countSmsLink) {
                        var countSms = /В почте (\d+)/.
                                exec(countSmsLink.innerHTML);
                        testSms[2] = countSms ? countSms[1] : '';
                    }
                }

                var stData = general.getData(6),
                    playSound = new PlaySound();

                if ((testSms[0] || testSms[1]) && !stData[0]) {
                    stData[0] = '1';
                    playSound.init(stData[6]);
                    general.setData(stData, 6);
                } else if (!testSms[0] && !testSms[1] && stData[0]) {
                    stData[0] = '';
                    general.setData(stData, 6);
                }

                // ищем ссылку на объект где работаем/работали
                var cssSelector = 'td.font8pt[align="center"]',
                    linkObj = spanContent.querySelector(cssSelector);

                // видимо что-то случилось
                if (!linkObj) {
                    return;
                }

                var content = linkObj.innerHTML;
                linkObj = linkObj.querySelector('a[href^="/object.php?id="]');
                if (!linkObj) {
                    return;
                }

                // поиск ссылок на экипировку
                cssSelector = 'td[valign="bottom"][bgcolor="#e9ffe9"]' +
                    '[colspan="2"]';
                var items = spanContent.querySelector(cssSelector);
                items = items ? items.querySelectorAll('a') : false;

                // время до окончания работы
                var time;
                if (/(Вы сможете устроиться на|осталось)[^\d]*\d+ минут/i.
                        test(content)) {
                    time = +/(\d+) минут/i.exec(content)[1];
                    time = !time ? 1 : time;
                } else if (/Последний раз вы работали/i.test(content)) {
                    time = 0;
                } else if (/Вы работаете на/i.test(content)) {
                    time = 1;
                }

                // проверка на наличие грены
                var testGrenades = false;
                if (items && items.length) {
                    var itemId, i;
                    for (i = 0; i < items.length; i++) {
                        itemId = /\/item\.php\?item_id=(.*)$/.
                            exec(items[i].href);
                        if (itemId &&
                                _this.grenades.indexOf(itemId[1]) !== -1) {
                            testGrenades = true;
                            break;
                        }
                    }
                }

                var ttl = '" title="Объект #' +
                        /object\.php\?id=(\d+)/.exec(linkObj.href)[1] +
                        '" alt="GW объект" /></a>]';

                if (time) {
                    if (stData[2]) {
                        _this.wpgbContainer.innerHTML = '[<span style=' +
                            '"color: #0000FF;">' + time + '</span> мин ' +
                            '<a href="' + linkObj.href + '"><img alt="img" ' +
                            'src="' + _this.blueFactory + ttl;
                    }

                    stData[1] = '';
                    general.setData(stData, 6);
                } else {
                    if (stData[2]) {
                        _this.wpgbContainer.innerHTML = '[<a href="' +
                            linkObj.href + '"><img alt="img" src="' +
                            _this.redFactory + ttl;
                    }

                    if (!stData[1]) {
                        stData[1] = '1';
                        playSound.init(stData[7]);
                        general.setData(stData, 6);
                    }
                }

                cssSelector = 'a[href="/workshop.php"][style$="#990000;"]';
                var testBroken = spanContent.querySelector(cssSelector) ||
                        false;
                _this.wpgbContainer.innerHTML += _this.addContent(testSms,
                    testGrenades, testBroken);

            }, function () {
                general.root.setTimeout(function () {
                    _this.startWorkPostGrenadesBroken(_this);
                }, 1000);
            });
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            var topPanel = new GetTopPanel().init();
            // noinspection JSUnresolvedVariable
            if (topPanel) {
                topPanel.appendChild(general.doc.createTextNode(' | '));
                topPanel.appendChild(this.wpgbContainer);

                this.startWorkPostGrenadesBroken(null);
                var _this = this;
                general.root.setInterval(function () {
                    _this.startWorkPostGrenadesBroken(_this);
                }, new GetRandom().init(20, 40) * 1000);
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class ResourcesAndBonuses {{{1
     * @constructor
     */
    var ResourcesAndBonuses = function () {
        /**
         * @property divResult
         * @type {HTMLElement}
         */
        this.divResult = general.doc.createElement('div');

        /**
         * @method fillData {{{2
         * @param   {String}    data
         */
        this.fillData = function (data) {
            this.divResult.innerHTML = data + '<div style="margin-top: 5px;">' +
                '<img id="divres_close" style="cursor: pointer;" ' +
                'src="' + general.imgPath + 'close.gif" alt="img" /></div>';

            var _this = this;
            general.$('divres_close').addEventListener('click', function () {
                _this.divResult.style.visibility = 'hidden';
            }, false);
        }; // 2}}}

        /**
         * @method showData {{{2
         * @param   {Object}    _this
         */
        this.showData = function (_this) {
            var pos = new GetPos().init(_this);

            this.divResult.style.left = pos.x;
            this.divResult.style.top = pos.y + 25;
            this.divResult.style.visibility = 'visible';
            this.divResult.innerHTML = '<img src="' + general.imgPath +
                'preloader.gif' + '" alt="img" />';

            var url = 'https://' + general.domain + '/info.php?id=' +
                    general.myID,
                idElem = _this.id,
                ths = this;

            new AjaxQuery().init(url, 'GET', null, true, function (xml) {
                var spanContent = general.doc.createElement('span');
                spanContent.innerHTML = xml.responseText;
                var cssSelector = 'td[class="greenbrightbg"][align="center"]' +
                        '[valign="top"]',
                    tables = spanContent.querySelectorAll(cssSelector),
                    data;

                data = idElem === 'res' ?
                        tables[0].innerHTML : tables[2].innerHTML;

                if (/Ресурсов в наличии нет/i.test(data)) {
                    data = '<span style="color: #0000FF;">Ресурсов в наличии ' +
                        'нет</span>';
                }

                ths.fillData(data);
            }, function () {
                ths.fillData('<span style="color: #FF0000;">' +
                    'Ошибка ответа сервера...</span>');
            });
        }; // 2}}}

        /**
         * @method createButton {{{2
         * @param   {String}    value
         * @param   {String}    id
         * @return  {HTMLElement}
         */
        this.createButton = function (value, id) {
            var span = general.doc.createElement('span'),
                _this = this;

            span.innerHTML = value;
            span.id = id;
            span.setAttribute('style', 'cursor: pointer;');
            span.addEventListener('click', function () {
                var ths = this;
                _this.showData(ths);
            }, false);
            return span;
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            var topPanel = new GetTopPanel().init();
            // noinspection JSUnresolvedVariable
            if (topPanel) {
                this.divResult.setAttribute('style', 'visibility: hidden; ' +
                        'position: absolute; padding: 3px; background-color: ' +
                        '#E7FFE7; border: solid 1px #339933; ' +
                        'border-radius:5px; top:0; left:0; box-shadow: ' +
                        '5px 6px 6px rgba(122,122,122,0.5); z-index: 999;');
                general.doc.body.appendChild(this.divResult);

                topPanel.appendChild(general.doc.createTextNode(' | '));
                topPanel.appendChild(this.createButton('Ресурсы', 'res'));
                topPanel.appendChild(general.doc.createTextNode(' | '));
                topPanel.appendChild(this.createButton('Бонусы', 'bonus'));
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class CritShotsAndLinksBtlLog {{{1
     * @constructor
     */
    var CritShotsAndLinksBtlLog = function () {
        /**
         * @property showCritShots
         * @type {String}
         */
        this.showCritShots = general.getData(7)[0];

        /**
         * @class CriticalShots {{{2
         * @constructor
         */
        this.CriticalShots = function () {
            /**
             * @property groin
             * @type {int}
             */
            this.groin = 0;     //пах
            /**
             * @property neck
             * @type {int}
             */
            this.neck = 0;      //шея
            /**
             * @property ear
             * @type {int}
             */
            this.ear = 0;       //ухо
            /**
             * @property temple
             * @type {int}
             */
            this.temple = 0;    //висок

            /**
             * @method getAllCrits
             * @return  {int}
             */
            this.getAllCrits = function () {
                return this.groin + this.neck + this.ear + this.temple;
            };
        }; // 2}}}

        /**
         * @method getCrits {{{2
         * @param   {NodeList}  b
         * @return  {Object|null}
         */
        this.getCrits = function (b) {
            var criticalShots = new this.CriticalShots(),
                myNik = general.doc.querySelector('a[href$="/info.php?id=' +
                    general.myID + '"]>b'),
                linkStyle,
                nik,
                font,
                str,
                node,
                next,
                prev,
                i;

            for (i = 0; i < b.length; i++) {
                // если это урон (-XX), 'vs', пок с '['
                if ((/^-\d+$|vs|\[|,/.test(b[i].innerHTML) &&
                        !/\[NPC\]/.test(b[i].innerHTML)) ||
                            /может взять предметы/.test(b[i].innerHTML)) {
                    continue;
                }

                // делаем ссылки на персов, если ссылка еще не установлена
                if (!b[i].querySelector('a:first-child') &&
                        !b[i].getAttribute('style')) {
                    linkStyle = 'text-decoration: none; font-weight: 700; ' +
                        'font-size: ' + (general.viewMode ? '12px;' : '11px;');

                    // Победа за ...
                    font = b[i].parentNode;
                    if (font.nodeName === 'FONT') {
                        linkStyle += ' color: ' + font.color + ';';
                    }

                    // если это окрашенный ник, то внутри будет <font>
                    font = b[i].querySelector('font:first-child');
                    if (font) {
                        linkStyle += ' color: ' + font.color + ';';
                        nik = font.innerHTML;
                    } else {
                        nik = b[i].innerHTML;
                    }

                    // личный NPC
                    if (/\[NPC\]/.test(b[i].innerHTML)) {
                        nik = b[i].innerText;
                        linkStyle += ' opacity: 0.6;';
                    }

                    // noinspection JSUndefinedPropertyAssignment
                    b[i].innerHTML = '<a target="_blank" style="' + linkStyle +
                        '"  href="https://' + general.domain +
                        '/search.php?key=' + nik + '">' + nik + '</a>';
                }

                // наш ник не найден или в настройках не установлен checkbox
                // "показывать критические выстрелы" или это не наш ник
                if (!myNik || !this.showCritShots ||
                        b[i].innerHTML.indexOf(myNik.innerHTML) === -1) {
                    continue;
                }

                // проверяем и считаем криты
                prev = b[i].previousSibling || b[i].parentNode.previousSibling;
                if (prev && prev.nodeValue &&
                        /\d+:\d+, #\d+ :/.test(prev.nodeValue)) {

                    // получаем запись своего хода
                    str = '';
                    node = b[i].previousSibling ? b[i] : b[i].parentNode;
                    while (node && node.nodeName !== 'BR') {
                        next = node.nextElementSibling;
                        if (next.nodeName === 'I') {
                            str += next.innerHTML;
                        }

                        node = next;
                    }

                    if (str) {
                        // считаем криты
                        if (/в пах/.test(str)) {
                            if (/\d+ в пах/.test(str)) {
                                criticalShots.groin += +/(\d+) в пах/.
                                            exec(str)[1];
                            } else {
                                criticalShots.groin++;
                            }
                        }

                        if (/в шею/.test(str)) {
                            if (/\d+ в шею/.test(str)) {
                                criticalShots.neck += +/(\d+) в шею/.
                                            exec(str)[1];
                            } else {
                                criticalShots.neck++;
                            }
                        }

                        if (/в ухо/.test(str)) {
                            if (/\d+ в ухо/.test(str)) {
                                criticalShots.ear += +/(\d+) в ухо/.
                                            exec(str)[1];
                            } else {
                                criticalShots.ear++;
                            }
                        }

                        if (/в висок/.test(str)) {
                            if (/\d+ в висок/.test(str)) {
                                criticalShots.temple += +/(\d+) в висок/.
                                            exec(str)[1];
                            } else {
                                criticalShots.temple++;
                            }
                        }
                    }
                }
            }

            return this.showCritShots ? criticalShots : null;
        }; // 2}}}

        /**
         * @method showCrits {{{2
         * @param   {Object}    result
         */
        this.showCrits = function (result) {
            if (result && result.getAllCrits()) {
                general.$('count_all_crits').innerHTML = result.getAllCrits();
                general.$('crits_groin').innerHTML = result.groin;
                general.$('crits_neck').innerHTML = result.neck;
                general.$('crits_ear').innerHTML = result.ear;
                general.$('crits_temple').innerHTML = result.temple;
            }
        }; // 2}}}

        /**
         * @method change_updatechatlines {{{2
         */
        this.change_updatechatlines = function () {
            var _this = this;

            general.root.updatechatlines = function () {
                /** @namespace general.root.frames.bsrc */
                var logDiv = general.root.frames.bsrc.document.
                        querySelector('#log'),
                    battleLog;

                if (logDiv && logDiv.childNodes.length) {
                    battleLog = general.doc.querySelector('#log');
                    battleLog.innerHTML = logDiv.innerHTML +
                        battleLog.innerHTML;
                    logDiv.innerHTML = '';
                    _this.showCrits(_this.getCrits(general.doc.
                            querySelector('#log').querySelectorAll('b')));
                }
            };
        }; // 2}}}

        /**
         * @method setDataDiv {{{2
         * @param   {Object}    target
         * @param   {Boolean}   mode
         */
        this.setDataDiv = function (target, mode) {
            // noinspection JSUnresolvedVariable
            if (this.showCritShots) {
                var d = general.doc.createElement('div');
                d.innerHTML = '<span style="color: #008000; font-weight: ' +
                    'bold;">Криты:</span> <span id="count_all_crits" ' +
                    'style="color: #FF0000; font-weight: bold;">0</span> ' +
                    '[ <span style="text-decoration: underline;">в пах:' +
                    '</span> <span id="crits_groin" style="color: ' +
                    '#FF0000;">0</span> <span style="text-decoration: ' +
                    'underline;">в шею:</span> <span id="crits_neck" ' +
                    'style="color: #FF0000;">0</span> <span style=' +
                    '"text-decoration: underline;">в ухо:</span> ' +
                    '<span id="crits_ear" style="color: #FF0000;">0</span> ' +
                    '<span style="text-decoration: underline;">в висок:' +
                    '</span> <span id="crits_temple" style="color: ' +
                    '#FF0000;">0</span> ]';
                target.appendChild(d);

                if (!mode) {    // в бою
                    target.appendChild(general.doc.createElement('br'));
                }
            }
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            // НЕ JS-версия боя
            if (/\/b0\/b\.php/.test(general.loc)) {
                return;
            }

            var chat = general.doc.querySelector('form[name="battlechat"]'),
                _this = this,
                log;

            if (!general.viewMode) {    // в бою
                // ждем загрузки данных на странице
                var lPers = general.$('listleft');
                log = general.$('log');
                if (!lPers || /загружаются данные/i.test(lPers.innerHTML) ||
                        !log || !chat) {
                    general.root.setTimeout(function () {
                        _this.init();
                    }, 200);
                    return;
                }

                this.setDataDiv(chat.parentNode, false);
                // изменяем функцию обновления чата на странице
                this.change_updatechatlines();
            } else {    // режим наблюдения за боем
                var center = general.doc.querySelector('td[valign="top"]' +
                        '[width="70%"]>center');
                center.appendChild(general.doc.createElement('br'));
                center.appendChild(general.doc.createElement('br'));
                this.setDataDiv(center, true);
                this.showCrits(this.getCrits(general.doc.
                        querySelector('td>span[class="txt"]').
                    querySelectorAll('b')));
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class DeleteSms {{{1
     * @constructor
     */
    var DeleteSms = function () {
        /**
         * @method testSubject {{{2
         * @param   {HTMLInputElement}  chk
         * @param   {Object}            reg
         * @return  {Boolean}
         */
        this.testSubject = function (chk, reg) {
            // noinspection JSUnresolvedVariable
            return reg.test(chk.parentNode.nextElementSibling.innerHTML);
        }; // 2}}}

        /**
         * @method checkSms {{{2
         * @param   {HTMLElement}   chk
         * @param   {Boolean}       is_check
         * @param   {Boolean}       load
         */
        this.checkSms = function (chk, is_check, load) {
            var stData = general.getData(8);
            // [0]  - отмечать синдовые рассылки
            // [1]  - отмечать рассылки от робота
            // [2]  - НЕ отмечать письма с пометкой "важное"

            if (is_check) {
                chk.checked = false;
                return;
            }

            var markSyndSms = stData[0],
                markRobotSms = stData[1],
                noMarkImportantSms = stData[2],
                no_check_important = this.testSubject(chk, /\[важное\]/) &&
                    noMarkImportantSms;

            if (this.testSubject(chk, /<b>#\d+<\/b>/)) {
                // синдрассылка
                if (!no_check_important) {
                    chk.checked = !!markSyndSms;
                } else {
                    chk.checked = false;
                }
            } else if (chk.parentNode.parentNode.
                    querySelector('a[href="/info.php?id=1"]')) {
                // сообщение от робота
                if (!no_check_important) {
                    chk.checked = !!markRobotSms;
                } else {
                    chk.checked = false;
                }
            } else {
                // все остальные сообщения (только при нажатии [+])
                if (!no_check_important) {
                    chk.checked = !load;
                } else {
                    chk.checked = false;
                }
            }
        }; // 2}}}

        /**
         * @method get_checkbox_checked {{{2
         * @return  {HTMLElement|null}
         */
        this.get_checkbox_checked = function () {
            return general.doc.querySelector('input[type="checkbox"]' +
                '[name^="kill"]:checked');
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            var target = general.doc.querySelector('tr>td>a:last-child' +
                        '[href="/sms.php?page=2"]'),
                del = general.doc.querySelector('input[class="mainbutton"]' +
                    '[type="submit"][value="Удалить отмеченные"]'),
                smsChk = general.doc.querySelectorAll('input[type="checkbox"]' +
                    '[name^="kill"]'),
                i;

            if (!target || !del) {
                return;
            }

            // отмечаем сообщения в соответствии с настройками скрипта
            for (i = 0; i < smsChk.length; i++) {
                this.checkSms(smsChk[i], false, true);
            }

            target = target.parentNode.parentNode;
            target.innerHTML += '<td valign="top" class="greengreenbg" ' +
                'align="center" style="width: 150px;"></td>';
            target = target.lastElementChild;

            // кнопка удаления
            var delButton = general.doc.createElement('span');
            delButton.innerHTML = 'Удалить отмеченные';
            delButton.setAttribute('style', 'cursor: pointer; ' +
                    'text-decoration: underline;');
            delButton.addEventListener('click', function () {
                del.click();
            }, false);

            target.appendChild(delButton);

            // кнопка "Отметить все/Снять все"
            var markAll = general.doc.createElement('span'),
                checkbox_checked = this.get_checkbox_checked(),
                check_all = 'Отметить все',
                clear_all = 'Снять все',
                minus = '[&minus;]',
                plus = '[+]';

            markAll.setAttribute('style', 'margin-left: 5px; ' +
                    'cursor: pointer; color: #990000');
            markAll.innerHTML = checkbox_checked ? minus : plus;
            markAll.title = checkbox_checked ? clear_all : check_all;
            target.appendChild(markAll);
            var _this = this;
            markAll.addEventListener('click', function () {
                var but = this,
                    is_check = _this.get_checkbox_checked();

                but.innerHTML = is_check ? plus : minus;
                but.title = is_check ? check_all : clear_all;

                var l;
                for (l = 0; l < smsChk.length; l++) {
                    _this.checkSms(smsChk[l], is_check, false);
                }
            }, false);
        }; // 2}}}
    }; // 1}}}

    /**
     * @class FarmExperience {{{1
     * @constructor
     */
    var FarmExperience = function () {
        /**
         * @method calculateFarm {{{2
         * @param   {int}       p1
         * @param   {int}       p2
         * @param   {int}       time
         * @param   {Number}    exp
         * @return  {String}
         */
        this.calculateFarm = function (p1, p2, time, exp) {
            var money = ((p2 - p1) / time * 60).toFixed(2),
                experience = (exp / time * 60).toFixed(3);

            return ' <span style="color: #FF0000;">[$' + money + ']</span>' +
                    '<span style="color: #0000FF;">[' + experience + ']</span>';
        }; // 2}}}

        /**
         * @method runInit {{{2
         */
        this.runInit = function () {
            var _this = this;
            return function () {
                general.root.setTimeout(function () {
                    _this.init();
                }, 700);
            };
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            var a = general.doc.querySelectorAll('*[onclick*="gotourl("],' +
                        '*[onclick*="openurl("],*[onclick*="plantit("]'),
                l;

            for (l = 0; l < a.length; l++) {
                a[l].addEventListener('click', this.runInit(), false);
            }

            // не на пустрой грядке
            if (!general.doc.querySelector('input[value="Посадить"]')) {
                return;
            }

            var plants = general.doc.querySelectorAll('table[cellspacing="0"]' +
                    '[cellpadding="0"]  td[valign="top"][onclick*="btn_"]'),
                price1,
                price2,
                target,
                time,
                span,
                exp,
                i;

            for (i = 0; i < plants.length; i++) {
                span = general.doc.createElement('span');
                span.setAttribute('style', 'font-size: 9px;');
                price1 = +/\$(\d+)/.exec(plants[i].querySelector('font' +
                    '[color="#006600"]>b:last-child').innerHTML)[1];
                price2 = +/\$(\d+)/.exec(plants[i].querySelector('font' +
                    '[color="#990000"]>b').innerHTML)[1];
                time = +/созревания:\s(\d+)/.exec(plants[i].innerHTML)[1];
                exp = parseFloat(/(\d+\.?\d*) опыта/.
                                    exec(plants[i].innerHTML)[1]);
                span.innerHTML = this.calculateFarm(price1, price2, time, exp);
                target = plants[i].querySelector('br');
                target.parentNode.insertBefore(span, target);
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class FarmTimer {{{1
     * @constructor
     */
    var FarmTimer = function () {
        /**
         * @property farmLink
         * @type {HTMLLinkElement|null}
         */
        this.farmLink = null;
        /**
         * @property checkInterval
         * @type {int}
         */
        this.checkInterval = 0;

        /**
         * @method setRedLink {{{2
         * @param   {String}    str
         */
        this.setRedLink = function (str) {
            var action = str === '2' ? 'Собрать' : 'Полить';
            this.farmLink.setAttribute('style', 'color: #FF0000; ' +
                    'font-weight: bold; text-decoration: none;');
            this.farmLink.innerHTML = '[' + action + ']';
        }; // 2}}}

        /**
         * @method checkState {{{2
         */
        this.checkState = function () {
            if (!/bold/.test(this.farmLink.getAttribute('style'))) {
                if (this.checkInterval) {
                    general.root.clearInterval(this.checkInterval);
                }

                return;
            }

            var stData = general.getData(9);
            if (!stData[0]) {
                this.farmLink.innerHTML = '';
                return;
            }

            var timeNow = new Date().getTime(),
                time = +stData[0];

            if (timeNow <= time) {
                this.farmLink.setAttribute('style', 'color: #0000FF; ' +
                        'text-decoration: none;');
                stData[3] = '';
                general.setData(stData, 9);
                this.showTimer(+((time - timeNow) / 1000).toFixed(0));
            }
        }; // 2}}}

        /**
         * @method setReminder {{{2
         */
        this.setReminder = function () {
            var stData = general.getData(9);
            if (!stData[0]) {
                this.farmLink.innerHTML = '';
                return;
            }

            this.setRedLink(stData[1]);

            var _this = this;
            this.checkInterval = general.root.setInterval(function () {
                _this.checkState();
            }, 2000);

            if (!stData[4]) {
                return;
            }

            var timeNow = new Date().getTime();
            if (timeNow < +stData[0]) {
                return;
            }

            var soundTime = +stData[2],
                intrvl = stData[5] ? +stData[5] * 1000 : 0,
                random = new GetRandom();

            // пора проигрывать звук
            if (timeNow - soundTime >= intrvl) {
                // в настройках указано не повторять звук
                if (!intrvl) {
                    // если звук еще не был проигран
                    if (!stData[3]) {
                        stData[3] = '1';
                        general.setData(stData, 9);
                        new PlaySound().init(stData[4]);
                    }

                    return;
                }

                stData[2] = timeNow;
                general.setData(stData, 9);
                general.root.setTimeout(function () {
                    _this.setReminder();
                }, intrvl + random.init(1000, 10000));

                new PlaySound().init(stData[4]);
            } else if (intrvl) {
                general.root.setTimeout(function () {
                    _this.setReminder();
                }, intrvl - (timeNow - soundTime) + random.init(1000, 10000));
            }
        }; // 2}}}

        /**
         * @method showTimer {{{2
         * @param   {int}   sec
         */
        this.showTimer = function (sec) {
            var min,
                s,
                h;

            if (!sec) {
                this.setReminder();
                return;
            }

            s = sec;
            h = Math.floor(s / 3600);
            s -= h * 3600;
            min = Math.floor(s / 60);
            s -= min * 60;

            this.farmLink.innerHTML = '[' + (h < 10 ? '0' + h : h) + ':' +
                (min < 10 ? '0' + min : min) + ':' +
                (s < 10 ? '0' + s : s) + ']';

            sec -= 1;
            if (sec > -1) {
                var _this = this;
                general.root.setTimeout(function () {
                    _this.showTimer(sec);
                }, 1000);
            } else {
                general.root.setTimeout(this.setReminder,
                                new GetRandom().init(1000, 3000));
            }
        }; // 2}}}

        /**
         * @method runInit {{{2
         */
        this.runInit = function () {
            var _this = this;
            return function () {
                general.root.setTimeout(function () {
                    _this.init();
                }, 700);
            };
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            /** localStorage:
             * [0] - время полива/сбора
             * [1] - действие (Полить|Собрать)
             * [2] - время последнего проигрывания звука
             * [3] - звук проигран?
             */
            var stData = general.getData(9),
                timeNow = new Date().getTime();

            // на ферме запоминаем данные, выходим
            if (/\/ferma\.php/.test(general.loc)) {
                // не на своей ферме
                if (/id=\d+/.test(general.loc) &&
                        /id=(\d+)/.exec(general.loc)[1] !== general.myID) {
                    return;
                }

                var a = general.doc.querySelectorAll('*[onclick*="gotourl("],' +
                            '*[onclick*="openurl("],*[onclick*="plantit("]'),
                    l;

                for (l = 0; l < a.length; l++) {
                    a[l].addEventListener('click', this.runInit(), false);
                }

                var actionStr = /Ближайшее действие:.*(собрать|полить).*\(.*\)/.
                        exec(general.doc.querySelector('td[width="400"]' +
                                '[valign="top"]').innerHTML);

                if (!actionStr) {
                    stData[0] = '';
                    general.setData(stData, 9);
                    return;
                }

                var aStr = actionStr[0];
                var action = /собрать/.test(aStr) ? '2' : '1';

                if (/уже пора/.test(aStr)) {
                    general.setData([timeNow, action, timeNow, '1',
                        stData[4], stData[5]], 9);
                    return;
                }

                var timeLeft = +/через (\d+) мин/.exec(aStr)[1];
                general.setData([timeNow + timeLeft * 60 * 1000,
                    action, timeNow, '', stData[4], stData[5]], 9);

                return;
            }

            var topPanel = new GetTopPanel().init();
            if (topPanel && stData[0]) {
                this.farmLink = general.doc.createElement('a');
                this.farmLink.setAttribute('style', 'color: #0000FF; ' +
                        'text-decoration: none;');
                this.farmLink.href = 'https://' + general.domain +
                    '/ferma.php?id=' + general.myID;
                this.farmLink.setAttribute('target', '_blank');
                topPanel.appendChild(general.doc.createTextNode(' | '));
                topPanel.appendChild(this.farmLink);

                if (timeNow >= +stData[0]) {
                    this.setRedLink(stData[1]);
                    var _this = this;
                    general.root.setTimeout(function () {
                        _this.showTimer(0);
                    }, new GetRandom().init(1000, 3000));

                    return;
                }

                this.showTimer(+((+stData[0] - timeNow) / 1000).toFixed(0));
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class TimeNpc {{{1
     * @constructor
     */
    var TimeNpc = function () {
        /**
         * @property imgPath
         * @type {String}
         */
        this.imgPath = general.imgPath + 'TimeNpc/';
        /**
         * @property npc {{{2
         * @type {Object}
         */
        this.npc = {
            g: ['2', '3', '6', '8', '10', '12'],
            z: ['1', '4', '5', '7', '9', '11'],
            p: ['16', '17', '18', '19', '20']
        }; // 2}}}
        /**
         * @property tm
         * @type {int}
         */
        this.tm = 1000;

        /**
         * @method clearNPCData {{{2
         */
        this.clearNPCData = function () {
            general.$('dataNPC').innerHTML = '';
        }; // 2}}}

        /**
         * @method setCloseButton {{{2
         */
        this.setCloseButton = function () {
            general.$('dataNPC').innerHTML += '<tr><td colspan="3" ' +
                'style="text-align: right;"><img id="npsDataClose" ' +
                'style="cursor: pointer; padding-top: 5px;" src="' +
                general.imgPath + 'close.gif" title="Очистить данные NPC" ' +
                'alt="Очистить данные NPC" /></td></tr>';

            general.$('npsDataClose').
                addEventListener('click', this.clearNPCData, false);
            general.$('imgSoundNPC').removeAttribute('checkscan');
        }; // 2}}}

        /**
         * @method  scanNPC {{{2
         * @param   {int}       ind
         * @param   {Array}     npcs
         */
        this.scanNPC = function (ind, npcs) {
            if (!ind) {
                if (general.$('imgSoundNPC').getAttribute('checkscan')) {
                    return;
                }

                this.clearNPCData();
                general.$('imgSoundNPC').setAttribute('checkscan', 'yes');
            }

            var url = 'https://' + general.domain + '/npc.php?id=' + npcs[ind],
                _this = this,
                tr,
                td;

            tr = general.doc.createElement('tr');
            td = general.doc.createElement('td');
            td.setAttribute('style', 'text-align: center;');
            td.setAttribute('colspan', '3');
            td.innerHTML = '<img src="' + general.imgPath + 'preloader.gif" ' +
                'title="Загрузка..." alt="Загрузка..." />';
            tr.appendChild(td);
            general.$('dataNPC').appendChild(tr);

            new AjaxQuery().init(url, 'GET', null, true, function (xml) {
                var spanContent = general.doc.createElement('span');
                spanContent.innerHTML = xml.responseText;
                if (/Вы находитесь в пути/.test(spanContent.innerHTML)) {
                    general.$('dataNPC').lastElementChild.innerHTML = '<td ' +
                        'style="color: #990000;">Вы в пути. Данные NPC не ' +
                        'доступны.</td>';
                    _this.setCloseButton();
                    return;
                }

                var cssSelector1 = 'td[class="wb"][colspan="3"]' +
                        '[bgcolor="#f0fff0"]',
                    cssSelector2 = 'a[href*="/syndicate.php?id="]',
                    cssSelector3 = 'td[class="wb"][align="left" ]' +
                        '[width="100%"]>b',
                    syndLink = spanContent.querySelector(cssSelector1).
                                querySelector(cssSelector2),
                    nameNPC = spanContent.querySelector(cssSelector3).innerHTML;

                general.$('dataNPC').lastElementChild.innerHTML = '<td>' +
                    '<a target="_blank" href="' + syndLink.href +
                    '"><img src="https://images.' +
                    general.domain.replace('www.', '') + '/img/synds/' +
                    /\?id=(\d+)/.exec(syndLink.href)[1] + '.gif" ' +
                    'alt="img" />' +
                    '</a></td><td><a target="_blank" href="' + url +
                    '" style="font-size: 8pt;">' + nameNPC + '</a></td>' +
                    '<td style="font-size: 8pt;">' +
                    syndLink.nextSibling.nodeValue.replace(/^\s*/, '') +
                    '</td>';

                ind++;
                if (npcs[ind]) {
                    general.root.setTimeout(function () {
                        _this.scanNPC(ind, npcs);
                    }, _this.tm);
                    return;
                }

                _this.setCloseButton();
            }, function () {
                general.root.setTimeout(function () {
                    if (!ind) {
                        general.$('imgSoundNPC').removeAttribute('checkscan');
                    }
                    _this.scanNPC(ind, npcs);
                }, _this.tm);
            });
        }; // 2}}}

        /**
         * @method getTimeNow {{{2
         * @return  {int}
         */
        this.getTimeNow = function () {
            return new Date().getTime();
        }; // 2}}}

        /**
         * @method goQuest {{{2
         */
        this.goQuest = function () {
            var stData = general.getData(10);
            general.$('timerNPC').innerHTML = '<a href="/npc.php?id=' +
                stData[1] + '" style="color: #1C9C03; font-weight: bold;">' +
                'Взять квест</a>';

            if (stData[0]) {
                new PlaySound().init(stData[3]);
            }
        }; // 2}}}

        /**
         * @method showTimerNPC {{{2
         * @param   {int}   sec
         */
        this.showTimerNPC = function (sec) {
            var timer = general.$('spanTimer');
            // при переходе на личного NPC таймера не будет
            if (timer) {
                var s = sec,
                    h = Math.floor(s / 3600);

                s -= h * 3600;
                var min = Math.floor(s / 60);
                s -= min * 60;

                h = h < 10 ? '0' + h : h;
                min = min < 10 ? '0' + min : min;
                s = s < 10 ? '0' + s : s;
                timer.innerHTML = h + ':' + min + ':' + s;

                sec -= 1;
                var _this = this;
                if (sec > -1) {
                    general.root.setTimeout(function () {
                        _this.showTimerNPC(sec);
                    }, 1000);
                } else {
                    this.goQuest();
                }
            }
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            // на главной странице личного NPC
            if (/\?nid=\d+/.test(general.loc)) {
                return;
            }

            var stData = general.getData(10);

            if (/gwars.*\/me(\/|\.php)/.test(general.loc)) {
                var mainDiv = general.doc.createElement('div'),
                    target = general.doc.querySelector('td[rowspan="3"]' +
                        '[valign="top"][bgcolor="#e9ffe9"]>' +
                        'div[style="padding-left:5px"]>' +
                        'a[href$="/home.friends.php"]').previousElementSibling,
                    onoff = stData[0] ? 'On' : 'Off';

                mainDiv.setAttribute('style', 'font-size: 8pt; ' +
                        'padding-left: 16px;');
                target.parentNode.insertBefore(mainDiv, target);
                mainDiv.innerHTML = '<span id="buttonNPC_Z" style="cursor: ' +
                    'pointer;">[Z]</span><span id="buttonNPC_G" style="' +
                    'cursor: pointer;">[G]</span><span id="buttonNPC_P" ' +
                    'style="cursor: pointer;">[P]</span> <img ' +
                    'id="imgSoundNPC" src="' + this.imgPath + 'sound' + onoff +
                    '.png" style="cursor: pointer; vertical-align: middle;" ' +
                    'title="Sound  ' + onoff + '" alt="Sound ' + onoff +
                    '"  /><span id="timerNPC" style="margin-left: 5px;">' +
                    '</span><table id="dataNPC" style="width: 100%;"></table>';

                var _this = this;
                general.$('imgSoundNPC').addEventListener('click', function () {
                    var data = general.getData(10),
                        d = data[0],
                        s = d ? 'Sound Off' : 'Sound On',
                        img = this;

                    img.src = _this.imgPath +
                            (d ? 'soundOff.png' : 'soundOn.png');
                    img.setAttribute('title', s);
                    img.setAttribute('alt', s);
                    data[0] = d ? '' : '1';
                    general.setData(data, 10);
                }, false);

                general.$('buttonNPC_Z').addEventListener('click', function () {
                    _this.scanNPC(0, _this.npc.z);
                }, false);
                general.$('buttonNPC_G').addEventListener('click', function () {
                    _this.scanNPC(0, _this.npc.g);
                }, false);
                general.$('buttonNPC_P').addEventListener('click', function () {
                    _this.scanNPC(0, _this.npc.p);
                }, false);

                if (!stData[2]) {   // время не установлено
                    general.$('timerNPC').innerHTML = '<a href="' +
                        (!stData[1] ? '#' : '/npc.php?id=' + stData[1]) +
                        '" style="color: #8C7B02; font-size: 8pt;">' +
                        'Поговорите с NPC</a>';
                } else {
                    if (this.getTimeNow() >= stData[2]) {   // пора делать квест
                        this.goQuest();
                    } else {    // ждем
                        general.$('timerNPC').innerHTML = '<a href="' +
                            '/npc.php?id=' + stData[1] + '" style="color: ' +
                            'red; font-size: 8pt;">NPC</a>: ' +
                            '[<span id="spanTimer"></span>]';
                        var sec = Math.
                                ceil((+stData[2] - this.getTimeNow()) / 1000);
                        this.showTimerNPC(sec);
                    }
                }

                return;
            }

            var talkNPC = general.doc.
                    querySelector('td[class="wb"][valign="top"]'),
                talk = /\?id=(\d+)&talk=1/.exec(general.loc);

            // если id у NPC больше 20, то это не наш NPC
            if (talkNPC && talk && +talk[1] < 21) {

                stData[1] = talk[1];
                // говорим с NPC, но время квеста еще не пришло
                var timer = /\[подождите (\d+) мин/.exec(talkNPC.innerHTML);
                if (timer) {
                    stData[2] = +timer[1] * 60 * 1000 + this.getTimeNow();
                    general.setData(stData, 10);
                    return;
                }

                // если берем или отказываемся от квеста,
                // то стираем время из хранилища
                if (/Ваш ответ:/.test(talkNPC.innerHTML)) {
                    stData[2] = '';
                    general.setData(stData, 10);
                }
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class AllPlantsOnFarm {{{1
     * @constructor
     */
    var AllPlantsOnFarm = function () {
        /**
         * @property target
         * @type {Object|null}
         */
        this.target = null;
        /**
         * @property plant {{{2
         * @type {Object}
         */
        this.plant = {
            'ukrop': {name: 'Укроп', price: 5, time: 12, bonus: 6, exp: 0.007},
            'opyata': {name: 'Странные опята', price: 10, time: 376, bonus: 40,
                exp: 0.204},
            'tulips': {name: 'Тюльпаны', price: 20, time: 67, bonus: 26,
                exp: 0.04},
            'mak': {name: 'Полевой мак', price: 12, time: 27, bonus: 15,
                exp: 0.02},
            'muhomor': {name: 'Мухомор', price: 150, time: 150, bonus: 165,
                exp: 0.098},
            'podsolnuh': {name: 'Подсолнух', price: 50, time: 126, bonus: 62,
                exp: 0.082},
            'kaktus': {name: 'Коричневый кактус', price: 160, time: 376,
                bonus: 197, exp: 0.245},
            'geran': {name: 'Герань', price: 10, time: 57, bonus: 17,
                exp: 0.048},
            'tabak': {name: 'Веселый табак', price: 20, time: 150, bonus: 37,
                exp: 0.114},
            'korica': {name: 'Корица забористая', price: 16, time: 68,
                bonus: 25, exp: 0.057},
            'hren': {name: 'Хрен', price: 210, time: 300, bonus: 244,
                exp: 0.229},
            'baklajan': {name: 'Баклажан', price: 150, time: 226, bonus: 176,
                exp: 0.171},
            'chai': {name: 'Зеленый чай', price: 50, time: 90, bonus: 63,
                exp: 0.087},
            'aloe': {name: 'Алоэ', price: 120, time: 45, bonus: 127,
                exp: 0.044},
            'ogurets': {name: 'Огурцы', price: 350, time: 68, bonus: 360,
                exp: 0.065},
            'klubnika': {name: 'Клубника', price: 100, time: 450, bonus: 159,
                exp: 0.392},
            'malina': {name: 'Малина', price: 190, time: 90, bonus: 203,
                exp: 0.087},
            'shalfei': {name: 'Задумчивый шалфей&nbsp;', price: 800, time: 715,
                bonus: 885, exp: 0.566},
            'mint': {name: 'Мята', price: 50, time: 34, bonus: 56, exp: 0.037},
            'kokos': {name: 'Кокосовая пальма', price: 200, time: 176,
                bonus: 226, exp: 0.171},
            'vinograd': {name: 'Виноград', price: 140, time: 90, bonus: 155,
                exp: 0.098},
            'tabak2': {name: 'Жевательный табак', price: 170, time: 29,
                bonus: 175, exp: 0.031},
            'whitemush': {name: 'Белые грибы', price: 120, time: 126,
                bonus: 138, exp: 0.122},
            'kapusta': {name: 'Капуста', price: 150, time: 79, bonus: 164,
                exp: 0.095},
            'kust': {name: 'Хвойный куст', price: 600, time: 350, bonus: 657,
                exp: 0.381},
            'kabachok': {name: 'Кабачок', price: 140, time: 95, bonus: 155,
                exp: 0.102},
            'kukuruza': {name: 'Кукуруза', price: 170, time: 68, bonus: 182,
                exp: 0.082},
            'jahntak': {name: 'Джантак', price: 130, time: 164, bonus: 157,
                exp: 0.177},
            'kaktusi': {name: 'Кактусы пушистые ', price: 1100, time: 634,
                bonus: 1194, exp: 0.626},
            'perets': {name: 'Красный перец', price: 180, time: 51, bonus: 191,
                exp: 0.073},
            'petrushka': {name: 'Петрушка курчавая ', price: 145, time: 29,
                bonus: 151, exp: 0.041},
            'tomat': {name: 'Говорящий томат', price: 500, time: 276,
                bonus: 554, exp: 0.359},
            'arbuz': {name: 'Арбуз', price: 100, time: 45, bonus: 111,
                exp: 0.071},
            'hmel': {name: 'Душистый хмель', price: 130, time: 23, bonus: 135,
                exp: 0.035},
            'bambuk': {name: 'Ростки Бамбука', price: 10, time: 17, bonus: 14,
                exp: 0.029},
            'tikva': {name: 'Тыква', price: 200, time: 57, bonus: 214,
                exp: 0.095},
            'shishki': {name: 'Еловые шишки', price: 250, time: 45, bonus: 262,
                exp: 0.082},
            'dinya': {name: 'Дыня', price: 120, time: 88, bonus: 141,
                exp: 0.143},
            'podsolnuh2': {name: 'Подсолнух-мутант', price: 200, time: 50,
                bonus: 213, exp: 0.087},
            'poganka': {name: 'Бледная поганка', price: 1500, time: 338,
                bonus: 1604, exp: 0.694}
        }; // 2}}}

        /**
         * @method getHourTime {{{2
         * @param   {int}       min
         * @param   {Boolean}   mode
         * @return  {String}
         */
        this.getHourTime = function (min, mode) {
            if (min < 60) {
                return '';
            }

            var h = Math.floor(min / 60),
                m = min - h * 60;

            m = m < 10 ? '0' + m : m;
            return mode ? '(' + h + ' ч ' + m + ' мин)' :
                            '[' + h + ':' + m + ']';
        }; // 2}}}

        /**
         * @method calculatePerHour {{{2
         * @param   {int}   val1
         * @param   {int}   val2
         * @param   {int}   fix
         * @return  {String}
         */
        this.calculatePerHour = function (val1, val2, fix) {
            return (60 * val2 / val1).toFixed(fix);
        }; // 2}}}

        /**
         * @method setPlantData {{{2
         */
        this.setPlantData = function () {
            var cont = general.$('checkBoxContainer'),
                val = general.$('selPlant').value,
                id = 'btn_' + val,
                pl = this.plant[val],
                str = '';

            cont.innerHTML = '';
            // noinspection JSUnresolvedVariable
            if (val !== '0') {
                var i;
                for (i = 3; i < 7; i++) {
                    str += '<img src="https://images.' +
                        general.domain.replace('www.', '') +
                        '/img/ferma_hd/' + val + i + '.png" ' +
                        'style="width: 50px; height: 50px;" alt="img" />';
                }

                str += '<br><label for="' + id + '">' +
                    '<b style="color: #006600;">' + pl.name + ', ' + pl.price +
                    '$</b></label><br><li>Время созревания: ' +
                    '<b style="color: #990000;">' + pl.time + ' мин</b> ' +
                    this.getHourTime(pl.time, true) + '<li>Премия за урожай: ' +
                    '<b style="color: #990000;">' + pl.bonus + '$</b> ' +
                    '(<span style="color: #0000FF;">' +
                    this.calculatePerHour(pl.time, pl.bonus - pl.price, 2) +
                    '$</span> в час)<li>Производственный опыт: ' +
                    '<b style="color: #990000;">' + pl.exp + '</b> (' +
                    '<span style="color: #0000FF;">' +
                    this.calculatePerHour(pl.time, pl.exp, 3) +
                    '</span> в час)';

                cont.innerHTML = str;
            }
        }; // 2}}}

        /**
         * @method createSelectList {{{2
         * @return  {HTMLElement}
         */
        this.createSelectList = function () {
            var sel = general.doc.createElement('select'),
                str = '',
                val;

            for (val in this.plant) {
                if (this.plant.hasOwnProperty(val)) {
                    str += '<option value="' + val + '">' +
                        this.plant[val].name + '</option>';
                }
            }

            sel.id = 'selPlant';
            sel.innerHTML = '<option value="0"> </option>' + str;
            var _this = this;
            sel.addEventListener('change', function () {
                _this.setPlantData();
            }, false);

            return sel;
        }; // 2}}}

        /**
         * @method setMainPanel {{{2
         */
        this.setMainPanel = function () {
            // основной контейнер
            var div = this.target.querySelector('#allPlantContainer');
            if (!div) {
                div = general.doc.createElement('div');
                div.id = 'allPlantContainer';
                div.setAttribute('style', 'margin-top: 7px');
                if (this.target.nodeName === 'FORM') {
                    this.target.parentNode.lastElementChild.
                            firstElementChild.appendChild(div);
                } else {
                    this.target.appendChild(div);
                }
            }

            div.appendChild(this.createSelectList());

            var chkContainer = general.doc.createElement('div');
            chkContainer.setAttribute('align', 'left');
            chkContainer.id = 'checkBoxContainer';
            div.appendChild(chkContainer);
        }; // 2}}}

        /**
         * @method clearCounter {{{2
         * @param   {String}    gb
         * @param   {String}    exp
         */
        this.clearCounter = function (gb, exp) {
            var stData = general.getData(11);

            stData[1] = new Date().getTime();
            stData[2] = gb;
            stData[3] = exp;
            general.setData(stData, 11);
        }; // 2}}}

        /**
         * @method setCounter {{{2
         */
        this.setCounter = function () {
            var table = general.doc.querySelector('table[cellpadding="3"]' +
                    '[cellspacing="0"][border="0"][align="center"]'),
                prod = /получен опыт (\d+(\.\d+)?) ед/.exec(table.innerHTML);

            // опыт виден (на пустой вскопанной клетке)
            if (prod) {
                var target = table.querySelector('a[name="pf"]').parentNode,
                    gb = target.querySelectorAll('b'),
                    exp = prod[1],
                    stData = general.getData(11);

                // количесво Гб на ферме с учетом схрона
                // Счет: $85 + урожай на $6.6 в схроне, 1 шт.
                // noinspection RegExpSingleCharAlternation
                gb = +gb[0].innerHTML.replace(/\$|,/g, '') + (gb[1] ?
                        Math.round(parseFloat(gb[1].innerHTML.
                            replace(/\$|,/g, ''))) : 0);

                // время сброса не установлено
                if (!stData[1]) {
                    this.clearCounter(gb.toString(), exp);
                }

                // установка счетчиков
                var t = new Date(+stData[1]),
                    day = t.getDate(),
                    time = day < 10 ? '0' + day : day;

                time += '.';
                var month = t.getMonth() + 1;
                time += month < 10 ? '0' + month : month;
                time += '.';
                var year = /20(\d+)/.exec(t.getFullYear().toString())[1];
                time += year + ' ';
                var hours = t.getHours();
                time += hours < 10 ? '0' + hours : hours;
                time += ':';
                var min = t.getMinutes();
                time += min < 10 ? '0' + min : min;

                // noinspection JSRemoveUnnecessaryParentheses
                var setPoint = new SetPoints().init,
                    diffGb = gb - (+stData[2]),
                    diffExp = (+exp - (+stData[3])).toFixed(3).split('.');

                var str = '';
                if (stData[4]) {
                    str += '<span style="color: #' +
                        (diffGb < 0 ? '0000FF' : 'FF0000') + ';">' +
                        setPoint(diffGb, '\'', true) + '$</span>';
                }

                if (stData[5]) {
                    str += '<span' +
                        (str ? ' style="margin-left: 5px;"' : '') + '>' +
                        setPoint(diffExp[0], '\'', false) +
                        (diffExp[1] ? ',' + diffExp[1] : '') + '</span>';

                }

                str += '<span style="font-size: 7pt; margin-left: 5px;">' +
                            '<span id="clearFarmCounter" ' +
                                'style="cursor: pointer; color: #008000; ' +
                                'text-decoration: underline;">Сброс</span> ' +
                            '<span style="color: #0000FF;">(' + time + ')' +
                            '</span>' +
                        '</span>';

                var spanCounters = general.doc.createElement('span');
                spanCounters.setAttribute('style', 'font-size: 8pt; ' +
                    'margin-right: 5px;');
                spanCounters.innerHTML = str;
                target.insertBefore(spanCounters, target.firstElementChild);

                // кнопа сброса счетчиков
                var _this = this;
                general.$('clearFarmCounter').
                    addEventListener('click', function () {
                        if (confirm('Сбросить счетчики ?')) {
                            _this.clearCounter(gb.toString(), exp);
                            target.removeChild(spanCounters);
                            _this.setCounter();
                        }
                    }, false);
            }
        }; // 2}}}

        /**
         * @method runInit {{{2
         */
        this.runInit = function () {
            var _this = this;
            return function () {
                general.root.setTimeout(function () {
                    _this.init();
                }, 700);
            };
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            // noinspection RegExpSingleCharAlternation
            var farmId = /(\?|&)id=(\d+)/.exec(general.loc),
                capcha = general.doc.querySelector('input[type="hidden"]' +
                    '[name="captcha_question"]');

            this.target = general.doc.querySelector('td[width="400"]' +
                    '[valign="top"]');

            // нет капчи, на своей ферме
            if (!capcha && !(farmId && farmId[2] !== general.myID) &&
                    this.target) {

                var canPlant = general.doc.
                        querySelector('input[type="button"][value="Посадить"]');

                // счетчики Гб и производа
                var stData = general.getData(11);
                if (canPlant && (stData[4] || stData[5])) {
                    this.setCounter();
                }

                // перевод дохрена минут в чч:мм
                if (!canPlant) {
                    var tbl = general.doc.querySelector('table[width="100%"]' +
                            '[cellpadding="4"][cellspacing="2"][border="0"]');

                    if (tbl) {
                        var tds = tbl.querySelectorAll('td'),
                            span,
                            min,
                            i;

                        for (i = 1; i < tds.length; i++) {
                            min = /(через|осталось) (\d+) мин/.
                                exec(tds[i].innerHTML);

                            min = min && +min[2] > 59 ? +min[2] : null;
                            if (min) {
                                span = general.doc.createElement('span');
                                span.setAttribute('style', 'color: #0000FF; ' +
                                        'font-size: 7pt; margin-left: 2px;');
                                span.innerHTML = this.getHourTime(min, false);
                                tds[i].appendChild(span);
                            }
                        }
                    }

                    this.setMainPanel();
                }

                var a = general.doc.querySelectorAll('*[onclick*="gotourl("],' +
                            '*[onclick*="openurl("],*[onclick*="plantit("]'),
                    l;

                for (l = 0; l < a.length; l++) {
                    a[l].addEventListener('click', this.runInit(), false);
                }
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class GwMenu {{{1
     * @constructor
     */
    var GwMenu = function () {
        /**
         * @property imgPath
         * @type {String}
         */
        this.imgPath = general.imgPath + 'GwMenu/';
        /**
         * @property correctXGwMenu
         * @type {int}
         */
        this.correctXGwMenu = /Opera/i.
                test(general.root.navigator.userAgent) ?  -1 : 0;
        /**
         * @property correctYGwMenu
         * @type {int}
         */
        this.correctYGwMenu = /Firefox/i.
                test(general.root.navigator.userAgent) ? -1 : 0;

        /**
         * @method setCSSGwMenu {{{2
         */
        this.setCSSGwMenu = function () {
            var cssStyle = general.doc.createElement('style');
            cssStyle.innerHTML = '#c1 {color: #F90332;}' +
                '#c2 {color: #014305; font-weight: bold;}' +
                '#c3 {color: #04830C; font-style: italic;}' +
                '#op {font-size: 7pt; opacity: 0.5;}' +
                '.gwm {position: absolute; cursor: default; z-index: 999;}' +
                '.gwm table {border-collapse: collapse; background-color: ' +
                    '#DBF5E0; min-width: 100px; ' +
                    // тень для таблиц
                    // сдвиг по горизонтали, вертикали, радиус размытия, цвет
                    'box-shadow: 5px 6px 6px rgba(122,122,122,0.5);}' +
                '.gwm table.main {background-image: url(' + this.imgPath +
                        'background.gif' + '); width: 210px;}' +
                '.gwm tr {height: 15px;}' +
                '.gwm td {border: 1px #339933 solid; ' +
                    'padding: 0px 5px 0px 5px; white-space: nowrap;}' +
                '.gwm td.center {text-align: center;}' +
                '.gwm td.zag {text-align: center; background: #C4F8D1; ' +
                    'font-weight: bold; font-size: 11px;}' +
                '.gwm td.arrow {width: 20px; min-width: 20px; ' +
                    'text-align: center; background: #C4F8D1;}' +
                '.gwm td.bold {font-size: 8pt; font-weight: bold;}' +
                '.gwm td.bold span.norm {color: #087105;}' +
                '.gwm td.bold span.darkgray {color: #303030;}' +
                '.gwm td.bold span.crimson {color: #990000;}' +
                '.gwm td.bold span.brown {color: #935805;}' +
                '.gwm td.bold span.blue {color: #0000FF;}' +
                '.gwm td.bold span.bluegray {color: #008080;}' +
                '.gwm td.bold span.darkorange {color: #A44B00;}' +
                '.gwm a {text-decoration: none; color: #0000FF; ' +
                    'font-size: 8pt;}';

            general.doc.querySelector('head').appendChild(cssStyle);
        }; // 2}}}

        /**
         * @method createGwMenuDiv {{{2
         * @param   {String}    id
         * @return  {Object}
         */
        this.createGwMenuDiv = function (id) {
            var div = general.doc.createElement('div');

            if (id) {
                div.setAttribute('id', id);
                if (id === 'gw_menu') {
                    div.style.top = '83';
                    div.style.left = '0';
                    general.doc.body.appendChild(div);
                }
            }

            div.setAttribute('class', 'gwm');
            div.style.display = 'none';
            return div;
        }; // 2}}}

        /**
         * @method getLineMenu {{{2
         * @param   {String}    name
         * @param   {String}    id
         * @param   {String}    color
         * @param   {String}    colspn
         * @return  {String}
         */
        this.getLineMenu = function (name, id, color, colspn) {
            var link = /\//.test(id),
                _colspn,
                _color,
                blank;

            if (id === 'gw_menu') {
                return name;
            }

            // ссылка
            if (link) {
                blank = /https?:/.test(id) ? ' target="_blank"' : '';
                _color = color ? ' style="color: ' + color + '"' : '';
                _colspn = colspn ? ' colspan="2"' : '';
                return '<td' + _colspn + '><a' + blank + _color + ' href="' +
                    id + '">' + name + '</a></td>';
            }

            // пункты меню
            if (color === 'zag') {
                _colspn = colspn ? ' colspan="2"' : '';
                return '<td class="zag"' + _colspn + '>' + name + '</td>';
            }

            _color = color || 'norm';
            return '<td class="bold"><span class="' + _color + '">' + name +
                '</span></td><td class="arrow" id="' + id + '">&#187;</td>';
        }; // 2}}}

        /**
         * @method fOver {{{2
         * @param   {Object}    div
         * @param   {Object}    arrow
         * @param   {int}       offsettop
         */
        this.fOver = function (div, arrow, offsettop) {
            var tm = arrow.getAttribute('tm'),
                _offsettop = offsettop || 0;

            if (tm) {
                general.root.clearTimeout(+tm);
                arrow.removeAttribute('tm');
            }

            div.style.left = String(arrow.parentNode.offsetWidth +
                this.correctXGwMenu);
            div.style.top = String(arrow.offsetTop + _offsettop +
                this.correctYGwMenu);
            arrow.style.background = '#7FF89E';
            div.style.display = '';
        }; // 2}}}

        /**
         * @method fOut {{{2
         * @param   {Object}    div
         * @param   {Object}    arrow
         */
        this.fOut = function (div, arrow) {
            arrow.setAttribute('tm', general.root.setTimeout(function () {
                div.style.display = 'none';
                arrow.style.background = '#C4F8D1';
                arrow.removeAttribute('tm');
            }, 100).toString());
        }; // 2}}}

        /**
         * @method setHandlersGWMenu {{{2
         * @param   {Object}    arrow
         * @param   {Object}    div
         * @param   {int}       offset
         */
        this.setHandlersGWMenu = function (arrow, div, offset) {
            var _this = this;

            arrow.addEventListener('mouseover', function () {
                arrow.style.boxShadow = '1px 1px 1px inset';
                _this.fOver(div, arrow, offset);
            }, false);

            arrow.addEventListener('mouseout', function () {
                arrow.style.boxShadow = '';
                _this.fOut(div, arrow);
            }, false);

            div.addEventListener('mouseover', function () {
                arrow.style.boxShadow = '1px 1px 1px inset';
                _this.fOver(div, arrow, offset);
            }, false);

            div.addEventListener('mouseout', function () {
                arrow.style.boxShadow = '';
                _this.fOut(div, arrow);
            }, false);
        }; // 2}}}

        /**
         * @method createGWMenuItems {{{2
         * @param   {Array}    gwM
         */
        this.createGWMenuItems = function (gwM) {
            var item,
                main,
                tr,
                i,
                j;

            for (i = 0; i < gwM.length; i++) {
                gwM[i].divm = this.createGwMenuDiv(gwM[i].divm);
                gwM[i].divm.appendChild(general.doc.createElement('table'));

                main = gwM[i].divm.id === 'gw_menu';
                if (main) {
                    gwM[i].divm.firstElementChild.setAttribute('class', 'main');
                }

                for (j = 0; j < gwM[i].lines.length; j++) {
                    item = gwM[i].lines[j];
                    tr = general.doc.createElement('tr');
                    tr.innerHTML = this.
                        getLineMenu(item[0], item[1], item[2], item[3]);
                    gwM[i].divm.firstElementChild.appendChild(tr);
                }

                if (!main) {
                    general.$(gwM[i].prnt).appendChild(gwM[i].divm);
                    this.setHandlersGWMenu(general.$(gwM[i].arrow), gwM[i].divm,
                            gwM[i].offsetY);
                }
            }
        }; // 2}}}

        /**
         * @method gwMenuInit {{{2
         */
        this.gwMenuInit = function () {
            var gw_menu = general.$('gw_menu'),
                gwM;

            if (gw_menu) {
                gw_menu.style.display = gw_menu.style.display === '' ?
                        'none' : '';
                return;
            }

            this.setCSSGwMenu();

            gwM = [
                {divm: 'gw_menu', lines: [
                    // шапка
                    ['<td class="center"><a target="_blank" id="op" ' +
                        'href="/info.php?id=2095458"><span id="c1">' +
                        'developed by</span> <span id="c2">MyRequiem&#169;' +
                        '</span><br><span id="c3">for GWars fighters' +
                        '</span></a></td><td class="center" id="exit" ' +
                        'style="cursor: pointer;"><img src="' + this.imgPath +
                        'hide.gif" alt="Закрыть" title="Закрыть"></td>',
                        'gw_menu'],
                    // основные разделы
                    ['Ресурсы', 'resourses', 'brown'],
                    ['Бои', 'battles', 'crimson'],
                    ['ЭС, Уран', 'real_estate'],
                    ['Скрипты', 'scripts', 'darkorange'],
                    ['Синдикаты', 'syndicates'],
                    ['Острова', 'islands'],
                    ['NPC', 'npc'],
                    ['Доска объявлений', 'doska', 'bluegray'],
                    ['Почта', 'post', 'blue'],
                    ['Форумы', 'forums'],
                    ['Магазины', 'shop', 'brown'],
                    ['Покупка гранат', 'pay_grenades', 'brown'],
                    ['Покупка лута', 'pay_lut', 'brown'],
                    ['Переодевалка', 'changeclothing'],
                    ['Настройки', 'settings', 'darkgray'],
                    // ссылки
                    ['Мои фото', 'https://www.ganjafoto.io/albums.php?id=' +
                        general.myID, 0, 1],
                    ['Мои файлы', 'http://www.ganjafile.io/login.php', 0, 1],
                    ['Мои Друзья', '/home.friends.php', 0, 1],
                    ['Мои синдикаты', '/syndicates.php', 0, 1],
                    ['Мои достижения', '/info.ach.php?id=' +
                        general.myID, 0, 1],
                    ['Мои квесты', '/questlog.php?id=' + general.myID, 0, 1],
                    ['Mои навыки', '/home.skills.php?page=perks', 0, 1],
                    ['Протокол передач', '/usertransfers.php?id=' +
                        general.myID, 0, 1],
                    ['Протокол боев', '/info.warstats.php?id=' +
                        general.myID, 0, 1],
                    ['Моя карма', '/info.vote.php?id=' + general.myID, 0, 1],
                    ['Ферма', '/ferma.php?id=' + general.myID, 0, 1],
                    ['Суперсеты', '/sets.php', 0, 1],
                    ['GanjaWiki.ru: Энциклопедия игры',
                        'http://www.ganjawiki.ru/', 0, 1],
                    ['Выход из игры', '/logoff.php', 'red', 1],
                    ['<td colspan="2"><input type="checkbox" id="showt" ' +
                        'title="Показывать всегда" />', 'gw_menu']
                ]},
                {divm: 0, lines: [
                    ['Все ресурсы', '/stats.php', '#935805'],
                    ['Батареи', '/statlist.php?r=battery'],
                    ['Водоросли', '/statlist.php?r=seaweed'],
                    ['Нефть', '/statlist.php?r=oil'],
                    ['Уран', '/statlist.php?r=uran'],
                    ['Ганджиум', '/statlist.php?r=ganjium'],
                    ['Маковая соломка', '/statlist.php?r=solomka'],
                    ['Трава', '/statlist.php?r=weed'],
                    ['Бокситы', '/statlist.php?r=bauxite'],
                    ['Алюминий', '/statlist.php?r=aluminium']
                ], prnt: 'gw_menu', arrow: 'resourses', offsetY: -15},
                {divm: 'battles_1', lines: [
                    ['Одиночные', 'battles_1_single'],
                    ['Групповые', 'battles_1_group'],
                    ['Идущие бои', '/war.php', 0, 1]
                ], prnt: 'gw_menu', arrow: 'battles', offsetY: -15},
                {divm: 0, lines: [
                    ['Общие', '/warlist.php?war=armed'],
                    ['Стрельба из спецоружия', '/warlist.php?war=rogatki'],
                    ['Дуэли', '/warlist.php?war=duels']
                ], prnt: 'battles_1', arrow: 'battles_1_single'},
                {divm: 0, lines: [
                    ['Общие', '/wargroup.php?war=armed'],
                    ['Уличные', '/wargroup.php?war=street'],
                    ['Дуэли на спецоружии', '/wargroup.php?war=duels'],
                    ['Нападения', '/wargroup.php?war=attacks']
                ], prnt: 'battles_1', arrow: 'battles_1_group', offsetY: -15},
                {divm: 0, lines: [
                    ['ГосЭнергоАтом', '/info.realty.php?id=2'],
                    ['Статистика GWars.io от vasena',
                        'http://gw-utils.ru/'],
                    ['GWTools от Bas', 'https://www.gwtools.ru/']
                ], prnt: 'gw_menu', arrow: 'real_estate'},
                {divm: 0, lines: [
                    ['Синдикат "Скрипты для GW"', '/syndicate.php?id=3579'],
                    ['Скрипты на ganjascript.ucoz.com',
                        'http://ganjascript.ucoz.com/'],
                    ['Скрипты на gwscripts.ucoz.net',
                        'http://gwscripts.ucoz.net/'],
                    ['ComfortablePlayingInGW',
                        'https://github.com/MyRequiem/comfortablePlayingInGW'],
                    ['Скрипты на born2kill.clan.su',
                        'http://born2kill.clan.su/load/9'],
                    ['Скрипты от W_or_M',
                        'https://www.ganjafoto.io/image.php?aid=435039'],
                    ['Скрипты от Bick',
                        'https://www.ganjafoto.io/image.php?aid=331880'],
                    ['Скрипты от Jimmy Banditto',
                        'https://www.ganjafoto.io/image.php?aid=334909'],
                    ['Скрипты от VSOP_juDGe',
                        'https://www.ganjafoto.io/image.php?aid=260018'],
                    ['Скрипты от z0man',
                        'https://www.ganjafoto.io/image.php?aid=285332'],
                    ['Скрипты от гном убийца',
                        'https://www.ganjafoto.io/image.php?aid=256649']
                ], prnt: 'gw_menu', arrow: 'scripts', offsetY: -45},
                {divm: 'syndicates_1', lines: [
                    ['Официальные синдикаты', 'offic_synd'],
                    ['Рейтинг синдикатов', '/srating.php', 0, 1],
                    ['Поиск синдиката',
                        'http://www.cccp-gw.su/listsynd/listsynd_search.php',
                        0, 1]
                ], prnt: 'gw_menu', arrow: 'syndicates', offsetY: -15},
                {divm: 'offic_synd_1', lines: [
                    ['Администраторы игры', '/syndicate.php?id=3'],
                    ['GW - Редакторы описания игры', '/syndicate.php?id=1323'],
                    ['GW - Помощники администраторов',
                        '/syndicate.php?id=2076'],
                    ['GW - Почетный легион', '/syndicate.php?id=1320'],
                    ['GW - Технические персонажи', '/syndicate.php?id=445'],
                    ['GW - Bugtesters', '/syndicate.php?id=1949'],
                    ['GW - Суд', '/syndicate.php?id=1318'],
                    ['GW - Суд по взломам', '/syndicate.php?id=4409'],
                    ['GW - Суд // Common', '/syndicate.php?id=1953'],
                    ['GW - Суд - Ветераны', '/syndicate.php?id=1914'],
                    ['GW - Мировой Суд', '/syndicate.php?id=3060'],
                    ['GW - Судебные приставы', '/syndicate.php?id=1920'],
                    ['GW - Следователи', '/syndicate.php?id=2309'],
                    ['GW - Коллегия адвокатов', '/syndicate.php?id=1948'],
                    ['GW - Полиция', '/syndicate.php?id=1321'],
                    ['GW - Загс', '/syndicate.php?id=1354'],
                    ['GW - 911', '/syndicate.php?id=911'],
                    ['GW - GanjaWiki', '/syndicate.php?id=6949'],
                    ['GW - Модераторы GanjaFoto.io', '/syndicate.php?id=3516'],
                    ['GW - Модераторы рейтинга сайтов',
                        '/syndicate.php?id=3516'],
                    ['GW - Модераторы чата', '/syndicate.php?id=274'],
                    ['GW - Модераторы форума', '/syndicate.php?id=1262'],
                    ['GW - Модераторы торговых форумов',
                        '/syndicate.php?id=3405'],
                    ['ЗАО "Букмекерская контора №1"', '/syndicate.php?id=909'],
                    ['GW - Маркетинговая служба игры', '/syndicate.php?id=2324']
                ], prnt: 'syndicates_1', arrow: 'offic_synd', offsetY: -45},
                {divm: 0, lines: [
                    ['Z', '/map.php?sx=150&sy=150'],
                    ['G', '/map.php?sx=50&sy=50'],
                    ['P', '/map.php?sx=123&sy=77'],
                    ['S', '/map.php?sx=100&sy=100'],
                    ['Аут', 'http://born2kill.clan.su/index/0-55']
                ], prnt: 'gw_menu', arrow: 'islands', offsetY: -15},
                {divm: 'npc_1', lines: [
                    ['Z', 'npcz'],
                    ['G', 'npcg'],
                    ['P', 'npcp'],
                    ['NPC-синдикаты', 'npcsynd']
                ], prnt: 'gw_menu', arrow: 'npc', offsetY: -15},
                {divm: 0, lines: [
                    ['Smokie Canablez', '/npc.php?id=1'],
                    ['Kenny Buzz', '/npc.php?id=4'],
                    ['Yoshinori Watanabe', '/npc.php?id=5'],
                    ['Rony James', '/npc.php?id=7'],
                    ['Tommy Morales', '/npc.php?id=9'],
                    ['Tony Brandino', '/npc.php?id=11']
                ], prnt: 'npc_1', arrow: 'npcz', offsetY: -15},
                {divm: 0, lines: [
                    ['Hempy Trown', '/npc.php?id=2'],
                    ['Rusty Reefer', '/npc.php?id=3'],
                    ['Donnie Ray', '/npc.php?id=6'],
                    ['Ricardo Gonzalez', '/npc.php?id=8'],
                    ['Inamoto Kanushi', '/npc.php?id=10'],
                    ['John Moretti', '/npc.php?id=12']
                ], prnt: 'npc_1', arrow: 'npcg', offsetY: -30},
                {divm: 0, lines: [
                    ['Takeshi Yamagata', '/npc.php?id=16'],
                    ['Michael Doyle', '/npc.php?id=17'],
                    ['Alfonso Morales', '/npc.php?id=18'],
                    ['Roy Fatico', '/npc.php?id=19'],
                    ['Giovanni Greco', '/npc.php?id=20']
                ], prnt: 'npc_1', arrow: 'npcp', offsetY: -30},
                {divm: 0, lines: [
                    ['Yakuza', '/syndicate.php?id=5862'],
                    ['Black Guerillaz', '/syndicate.php?id=5865'],
                    ['Colombian Cartel', '/syndicate.php?id=5863'],
                    ['Camorra', '/syndicate.php?id=5866'],
                    ['Rastafaras', '/syndicate.php?id=5867']
                ], prnt: 'npc_1', arrow: 'npcsynd', offsetY: -45},
                {divm: 0, lines: [
                    ['ДО', '/market.php'],
                    ['Разместить объявление', '/market-p.php'],
                    ['Мгновенная продажа', '/market-i.php'],
                    ['Мои объявления', '/market-l.php'],
                    ['Предметы в аренде', '/info.rent.php?id=' + general.myID]
                ], prnt: 'gw_menu', arrow: 'doska', offsetY: -30},
                {divm: 0, lines: [
                    ['Чаты', '/sms.php'],
                    ['Новое сообщение', '/sms-create.php'],
                    ['Удаление', '/sms.php?page=2']
                ], prnt: 'gw_menu', arrow: 'post', offsetY: -15},
                {divm: 'forums_1', lines: [
                    ['Основные форумы', '', 'zag', 1],
                    ['Официальные объявления', '/threads.php?fid=1', 0, 1],
                    ['Вопросы и помощь в игре', '/threads.php?fid=49', 0, 1],
                    ['Общий форум', '/threads.php?fid=27', 0, 1],
                    ['Идеи и предложения', '/threads.php?fid=2', 0, 1],
                    ['Форум для неигровых тем', '/threads.php?fid=22', 0, 1],
                    ['Клуб Нытиков', '/threads.php?fid=55', 0, 1],
                    ['Объявления синдикатов', '/threads.php?fid=38', 0, 1],
                    ['Вступлю в синдикат', '/threads.php?fid=56', 0, 1],
                    ['Тотализатор', '/threads.php?fid=5', 0, 1],
                    ['Конкурсы', '/threads.php?fid=3', 0, 1],
                    ['Благодарности и поздравления',
                        '/threads.php?fid=4', 0, 1],
                    ['Offline встречи', '/threads.php?fid=6', 0, 1],
                    ['Креатив', '/threads.php?fid=23', 0, 1],
                    ['Официальные объявления<br>налоговой инспекции',
                        '/threads.php?fid=24', 0, 1],
                    ['Торговые форумы', 'trade_forums', 'brown'],
                    ['Суд', '', 'zag', 1],
                    ['Официальные объявления суда',
                        '/threads.php?fid=11', 0, 1],
                    ['Зал суда', '/threads.php?fid=12', 0, 1],
                    ['Зал бракосочетаний', '/threads.php?fid=29', 0, 1],
                    ['Дворец Бракосочетания', '/threads.php?fid=50', 0, 1],
                    ['Технические вопросы', '', 'zag', 1],
                    ['Техническая поддержка', '/threads.php?fid=17', 0, 1],
                    ['Баги и глюки (общее)', '/threads.php?fid=18', 0, 1],
                    ['Баги и глюки (финансовые вопросы)',
                        '/threads.php?fid=19', 0, 1],
                    ['Проблемы с боями', '/threads.php?fid=20', 0, 1],
                    ['Проблемы с персонажами', '/threads.php?fid=33', 0, 1],
                    ['Автономные районы', '', 'zag', 1],
                    ['Respect Hill', '/threads.php?fid=25', 0, 1]
                ], prnt: 'gw_menu', arrow: 'forums', offsetY: -158},
                {divm: 0, lines: [
                    ['Торговля предметами : Оружие', '/threads.php?fid=7'],
                    ['Торговля предметами : Броня', '/threads.php?fid=36'],
                    ['Торговля предметами : Аксессуары', '/threads.php?fid=37'],
                    ['Торговля high-tech предметами', '/threads.php?fid=35'],
                    ['Торговля недвижимостью', '/threads.php?fid=34'],
                    ['Торговля предметами : Редкие вещи',
                        '/threads.php?fid=44'],
                    ['Торговля модифицированными предметами',
                        '/threads.php?fid=47'],
                    ['Торговля сломанными вещами', '/threads.php?fid=48'],
                    ['Аренда предметов', '/threads.php?fid=46'],
                    ['Аренда недвижимости', '/threads.php?fid=54'],
                    ['Торговля синдикатами', '/threads.php?fid=41'],
                    ['Поиск исполнителей', '/threads.php?fid=42']
                ], prnt: 'forums_1', arrow: 'trade_forums', offsetY: -90},
                {divm: 'shop_1', lines: [
                    ['Гос. магазин', 'shop_gos'],
                    ['HighTech магазин', 'shop_hightech'],
                    ['Магазин аренды', 'shop_rent'],
                    ['Магазин синдиката', 'shop_synd'],
                    ['Магазин лицензий', '/shopl.php', 0, 1]
                ], prnt: 'gw_menu', arrow: 'shop', offsetY: -15},
                {divm: 0, lines: [
                    ['Огнестрельное оружие', '', 'zag'],
                    ['Пистолеты', '/shop.php?shop=shop_pistols'],
                    ['Автоматы', '/shop.php?shop=shop_auto'],
                    ['Снайперское оружие', '/shop.php?shop=shop_sniper'],
                    ['ПП', '/shop.php?shop=shop_ppguns'],
                    ['Пулемёты', '/shop.php?shop=shop_heavy'],
                    ['Дробовики', '/shop.php?shop=shop_shotguns'],
                    ['Гранатометы', '/shop.php?shop=shop_grl'],
                    ['Гранаты', '/shop.php?shop=shop_grenades'],
                    ['Специальное оружие', '/shop.php?shop=shop_misc'],
                    ['Полезные предметы', '', 'zag'],
                    ['Броня', '/shop.php?shop=shop_armour'],
                    ['Шлемы', '/shop.php?shop=shop_helmets'],
                    ['Броня ног', '/shop.php?shop=shop_boots'],
                    ['Маскировка', '/shop.php?shop=shop_masks'],
                    ['Тепловизоры', '/shop.php?shop=shop_wear'],
                    ['Пояса', '/shop.php?shop=shop_belts'],
                    ['Правый карман', '/shop.php?shop=shop_rightpocket'],
                    ['Левый карман', '/shop.php?shop=shop_leftpocket'],
                    ['Доп.карманы', '/shop.php?shop=shop_epockets'],
                    ['Транспорт', '/shop.php?shop=shop_transport'],
                    ['Цветы', '/shop.php?shop=shop_flowers'],
                    ['Аптека', '/shop.php?shop=shop_drugs'],
                    ['Сувениры', '/shop.php?shop=shop_gifts']
                ], prnt: 'shop_1', arrow: 'shop_gos', offsetY: -75},
                {divm: 0, lines: [
                    ['Огнестрельное оружие', '', 'zag'],
                    ['Автоматы', '/shopc.php?shop=shop_auto_c'],
                    ['Пулемёты', '/shopc.php?shop=shop_heavy_c'],
                    ['Снайперское оружие', '/shopc.php?shop=shop_snipe_c'],
                    ['ПП', '/shopc.php?shop=shop_ppguns_c'],
                    ['Дробовики', '/shopc.php?shop=shop_shotguns_c'],
                    ['Гранатометы', '/shopc.php?shop=shop_grl_c'],
                    ['Полезные предметы', '', 'zag'],
                    ['Броня', '/shopc.php?shop=shop_armour_c'],
                    ['Шлемы', '/shopc.php?shop=shop_helmets_c'],
                    ['Броня ног', '/shopc.php?shop=shop_boots_c'],
                    ['Маскировка', '/shopc.php?shop=shop_masks_c'],
                    ['Тепловизоры', '/shopc.php?shop=shop_wear_c'],
                    ['Пояса', '/shopc.php?shop=shop_belts_c'],
                    ['Правый карман', '/shopc.php?shop=shop_rightpocket_c'],
                    ['Левый карман', '/shopc.php?shop=shop_leftpocket_c'],
                    ['Доп.карманы', '/shopc.php?shop=shop_epockets_c'],
                    ['Аптека', '/shopc.php?shop=shop_drugs_c'],
                    ['Транспорт', '/shopc.php?shop=shop_transport_c']
                ], prnt: 'shop_1', arrow: 'shop_hightech', offsetY: -75},
                {divm: 0, lines: [
                    ['Rent-A-Gun', '', 'zag'],
                    ['Пулеметы', '/rent-a-gun.php?shop=shop_heavy_cr'],
                    ['Автоматы', '/rent-a-gun.php?shop=shop_auto_cr'],
                    ['Снайперское оружие',
                        '/rent-a-gun.php?shop=shop_snipe_cr'],
                    ['Экипировка', '/rent-a-gun.php?shop=shop_armour_cr']
                ], prnt: 'shop_1', arrow: 'shop_rent', offsetY: -30},
                {divm: 0, lines: [
                    ['Гранаты', '/sshop.php?tshop=grenades'],
                    ['Чипы на урон', '/sshop.php?tshop=chipsets'],
                    ['Чипы защитные', '/sshop.php?tshop=chipsets1'],
                    ['Чипы специальные', '/sshop.php?tshop=chipsets2'],
                    ['Турели', '/sshop.php?tshop=turrels'],
                    ['Звания', '/sshop.php?tshop=ranks']
                ], prnt: 'shop_1', arrow: 'shop_synd', offsetY: -15},
                {divm: 'pay_grenades_1', lines: [
                    ['Свет', 'ligntgr'],
                    ['Дым', 'smokegr'],
                    ['Электромагнитные', 'em_gr'],
                    ['Боевые', 'fightinggr']
                ], prnt: 'gw_menu', arrow: 'pay_grenades', offsetY: -15},
                {divm: 0, lines: [
                    ['ОР-1Т', '/market.php?buy=1&item_id=lightst'],
                    ['ОР-1', '/market.php?buy=1&item_id=lights'],
                    ['M84', '/market.php?buy=1&item_id=m84'],
                    ['Синдовые', '', 'zag'],
                    ['ОР-1С', '/statlist.php?r=lightss&type=i'],
                    ['ОР-2C', '/statlist.php?r=lightsm&type=i']
                ], prnt: 'pay_grenades_1', arrow: 'ligntgr', offsetY: -15},
                {divm: 0, lines: [
                    ['РГД-2', '/market.php?buy=1&item_id=rgd2'],
                    ['РГД-2М', '/market.php?buy=1&item_id=rgd2m'],
                    ['L83 A1 HG', '/market.php?buy=1&item_id=l83a1'],
                    ['Синдовые', '', 'zag'],
                    ['РГД-2С', '/statlist.php?r=rgd2s&type=i'],
                    ['ДГ-1', '/statlist.php?r=grenade_dg1&type=i']
                ], prnt: 'pay_grenades_1', arrow: 'smokegr', offsetY: -30},
                {divm: 0, lines: [
                    ['EMP-IR', '/market.php?buy=1&item_id=emp_ir'],
                    ['EMP-IRS', '/market.php?buy=1&item_id=emp_irs'],
                    ['EMP-A', '/market.php?buy=1&item_id=emp_a'],
                    ['EMP-S', '/market.php?buy=1&item_id=emp_s']
                ], prnt: 'pay_grenades_1', arrow: 'em_gr'},
                {divm: 0, lines: [
                    ['РГД-5', '/market.php?buy=1&item_id=rgd5'],
                    ['Граната F-1', '/market.php?buy=1&item_id=grenade_f1'],
                    ['РКГ-3', '/market.php?buy=1&item_id=rkg3'],
                    ['MDN', '/market.php?buy=1&item_id=mdn'],
                    ['РГО-1', '/market.php?buy=1&item_id=rgo'],
                    ['РГН', '/market.php?buy=1&item_id=rgn'],
                    ['Frag Grenade MK-3', '/market.php?buy=1&item_id=fg3l'],
                    ['M67', '/market.php?buy=1&item_id=m67'],
                    ['M3', '/market.php?buy=1&item_id=m3'],
                    ['HG-78', '/market.php?buy=1&item_id=hg78'],
                    ['HG-84', '/market.php?buy=1&item_id=hg84'],
                    ['Mk-6 Frag Grenade', '/market.php?buy=1&item_id=fg6'],
                    ['M14 Thermite', '/market.php?buy=1&item_id=anm14'],
                    ['M34 Ph', '/market.php?buy=1&item_id=m34ph'],
                    ['Mk-7 Frag Grenade', '/market.php?buy=1&item_id=fg7'],
                    ['Mk-8 Black Dust', '/market.php?buy=1&item_id=fg8bd'],
                    ['Синдовые', '', 'zag'],
                    ['GHTB', '/sshop.php?tshop=grenades'],
                    ['Mk-5 Frag Grenade', '/statlist.php?r=fg5&type=i'],
                    ['Коктейль Молотова', '/statlist.php?r=molotov&type=i'],
                    ['HellsBreath', '/statlist.php?r=hellsbreath&type=i'],
                    ['Напалм', '/statlist.php?r=napalm&type=i'],
                    ['ME-85 Frag Grenade', '/statlist.php?r=me85&type=i']
                ], prnt: 'pay_grenades_1', arrow: 'fightinggr', offsetY: -195},
                {divm: 'pay_lut_1', lines: [
                    ['Оружие и броня', 'weapon_armour_lut'],
                    ['Медикаменты', 'drugs_lut'],
                    ['Предметы', 'items_lut']
                ], prnt: 'gw_menu', arrow: 'pay_lut', offsetY: -15},
                {divm: 0, lines: [
                    ['Ржавая граната RGD-5',
                        '/market.php?buy=1&item_id=old_rgd5'],
                    ['Маскировочный плащ', '/market.php?buy=1&item_id=maskp'],
                    ['Тяжелые ботинки', '/market.php?buy=1&item_id=heavyboots'],
                    ['Шлем 2-го класса', '/market.php?buy=1&item_id=helmet2'],
                    ['Титановый пояс', '/market.php?buy=1&item_id=tbelt'],
                    ['HK-53', '/market.php?buy=1&item_id=hk53'],
                    ['L83 A1 HG', '/market.php?buy=1&item_id=l83a1'],
                    ['M84', '/market.php?buy=1&item_id=m84']
                ], prnt: 'pay_lut_1', arrow: 'weapon_armour_lut', offsetY: -45},
                {divm: 0, lines: [
                    ['Травяной сбор', '/market.php?buy=1&item_id=weedset'],
                    ['Вяленая рыба', '/market.php?buy=1&item_id=perch'],
                    ['Родниковая вода', '/market.php?buy=1&item_id=water'],
                    ['Грибы', '/market.php?buy=1&item_id=mushroom'],
                    ['Медицинский бинт', '/market.php?buy=1&item_id=bandage'],
                    ['Походная аптечка', '/market.php?buy=1&item_id=travelkit'],
                    ['Кофейные зерна', '/market.php?buy=1&item_id=coffee'],
                    ['Стимпак бессмертия',
                        '/market.php?buy=1&item_id=stimpack_iddqd'],
                    ['Стимпак бессмертия XL',
                        '/market.php?buy=1&item_id=stimpack_iddqd_xl'],
                    ['Стимпак брони',
                        '/market.php?buy=1&item_id=stimpack_armour'],
                    ['Стимпак брони XL',
                        '/market.php?buy=1&item_id=stimpack_armour_xl'],
                    ['Стимпак урона', '/market.php?buy=1&item_id=stimpack_dmg'],
                    ['Стимпак урона XL',
                        '/market.php?buy=1&item_id=stimpack_dmg_xl'],
                    ['Стимпак скорости',
                        '/market.php?buy=1&item_id=stimpack_spd']
                ], prnt: 'pay_lut_1', arrow: 'drugs_lut', offsetY: -90},
                {divm: 0, lines: [
                    ['Динамит', '/market.php?buy=1&item_id=dinamit'],
                    ['Книга опыта', '/market.php?buy=1&item_id=expbook'],
                    ['Журнал «Современное оружие»',
                        '/market.php?buy=1&item_id=magazine']
                ], prnt: 'pay_lut_1', arrow: 'items_lut', offsetY: -15},
                {divm: 0, lines: [
                    ['от Yeni', 'http://help.yeni.name/gan/'],
                    ['www.cccp-gw.su', 'http://www.cccp-gw.su/dress/']
                ], prnt: 'gw_menu', arrow: 'changeclothing'},
                {divm: 0, lines: [
                    ['Настройки', '/info.edit.php?type=main'],
                    ['Настройки боя', '/info.edit.php?type=battles'],
                    ['О себе', '/info.edit.php?type=pinfo'],
                    ['Анкета', '/info.anketa.php'],
                    ['E-mail и пароли', '/info.edit.php?type=security'],
                    ['Привязка к телефону', '/info.sms.php'],
                    ['Секретный ключ', '/info.edit.php?type=pkey'],
                    ['Аватар', '/info.edit.php?type=avatar'],
                    ['Приглашения', '/info.edit.php?type=invites'],
                    ['PDA', '/info.edit.php?type=pda'],
                    ['Навигация', '/info.edit.php?type=navy']
                ], prnt: 'gw_menu', arrow: 'settings', offsetY: -45}
            ];

            this.createGWMenuItems(gwM);

            var _this = this;
            //кнопка закрытия меню
            general.$('exit').addEventListener('click', function () {
                _this.gwMenuInit();
            }, false);

            //чекбокс "Показывать всегда"
            general.$('showt').addEventListener('click', function () {
                var showt = this;
                general.setData(showt.checked ? ['1'] : [], 12);
            }, false);

            this.gwMenuInit();
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            // ссылка в главном меню игры
            var target = general.doc.querySelector('a[href$="/ratings.php"]');
            if (target) {
                var mainLink = general.doc.createElement('span');
                mainLink.setAttribute('style', 'font-weight: bold; ' +
                    'cursor: pointer;');
                mainLink.innerHTML = 'GW-Меню';
                target.parentNode.insertBefore(mainLink, target);
                target.parentNode.
                    insertBefore(general.doc.createTextNode(' | '), target);

                var _this = this;
                mainLink.addEventListener('click', function () {
                    _this.gwMenuInit(_this);
                }, false);

                // если есть запись в хранилище "Показывать всегда"
                if (general.getData(12)[0]) {
                    this.gwMenuInit();
                    general.$('showt').checked = true;
                }
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class InventoryPlus {{{1
     * @constructor
     */
    var InventoryPlus = function () {
        /**
         * @method openCloseItem {{{2
         * @param   {String}    id
         */
        this.openCloseItem = function (id) {
            return function () {
                var tb = general.doc.getElementById('tr_' + id),
                    _this = this;

                if (tb.style.display === '') {
                    tb.style.display = 'none';
                    _this.innerHTML = _this.innerHTML.replace('-', '+');
                } else {
                    tb.style.display = '';
                    _this.innerHTML = _this.innerHTML.replace('+', '-');
                }
            };
        }; // 2}}}

        /**
         * @method compareLines {{{2
         * @param   {Object}        line
         * @param   {Array}         linesObj
         * @return  {Object|null}
         */
        this.compareLines = function (line, linesObj) {
            var i;

            line.link = line.line_1.querySelectorAll('a')[1];
            line.id = /id=(.*)$/.exec(line.link.href)[1];
            for (i = 0; i < linesObj.length; i++) {
                if (linesObj[i].line.id === line.id) {
                    return linesObj[i];
                }
            }

            return null;
        }; // 2}}}

        /**
         * @method startInventoryPlus {{{2
         * @param   {Object}    _this
         */
        this.startInventoryPlus = function (_this) {
            // ищем таблицу с инвентарем
            var tbody = general.doc.querySelector('table[border="0"]' +
                    '[cellspacing="1"][cellpadding="5"][align="center"]' +
                    '[width="700"] tr[id^="item_tr"]');

            if (!tbody) {
                return;
            }

            tbody = tbody.parentNode;
            if (!tbody.firstElementChild) {
                return;
            }

            var node = tbody.firstElementChild.nextElementSibling;
            if (/предметов нет/.test(node.innerHTML)) {
                return;
            }

            var allLines = [], // все узлы <tr> из инвентаря
                next,
                i;

            while (node) {
                i = allLines.length;
                allLines[i] = {'line_1': node.cloneNode(true), 'line_2': 0};
                next = node.nextSibling;
                if (next.nodeType === 1) {
                    allLines[i].line_2 = next.cloneNode(true);
                    node = next.nextElementSibling;
                } else {
                    node = node.nextElementSibling;
                }
            }

            // удаляем все предметы из инвентаря
            var trs = tbody.querySelectorAll('tr[id^="item_tr"]');
            for (i = 0; i < trs.length; i++) {
                tbody.removeChild(trs[i]);
            }

            // массив "уникальных" вещей (каждая вешь по одной и количество)
            var linesObj = [],
                obj;

            for (i = 0; i < allLines.length; i++) {
                obj = _this.compareLines(allLines[i], linesObj);
                if (!obj) {
                    linesObj[linesObj.length] = {line: allLines[i], count: 1};
                } else {
                    obj.count++;
                }
            }

            // вставляем вещи обратно в инвентарь
            var tblTarget,
                trHide,
                divn,
                id,
                td,
                j;

            for (i = 0; i < linesObj.length; i++) {
                tbody.appendChild(linesObj[i].line.line_1);
                if (linesObj[i].line.line_2) {
                    tbody.appendChild(linesObj[i].line.line_2);
                }

                // показываем количество только если оно больше 1
                if (linesObj[i].count !== 1) {
                    id = linesObj[i].line.id;
                    // вставим скрытые вещи
                    trHide = general.doc.createElement('tr');
                    trHide.id = 'tr_' + id;
                    trHide.setAttribute('style', 'display: none');
                    td = general.doc.createElement('td');
                    td.setAttribute('colspan', '5');
                    tblTarget = general.doc.createElement('table');
                    tblTarget.setAttribute('style',
                            'width: 100%; margin-left: 30px;');
                    td.appendChild(tblTarget);
                    trHide.appendChild(td);
                    tbody.appendChild(trHide);

                    for (j = 0; j < allLines.length; j++) {
                        if (allLines[j].id === id &&
                                linesObj[i].line.line_1.innerHTML !==
                                allLines[j].line_1.innerHTML) {
                            allLines[j].line_1.firstElementChild.
                                setAttribute('width', '400px');
                            tblTarget.appendChild(allLines[j].line_1);
                            if (allLines[j].line_2) {
                                tblTarget.appendChild(allLines[j].line_2);
                            }
                        }
                    }

                    //показываем количество и кнопу раскрытия списка
                    divn = general.doc.createElement('div');
                    divn.setAttribute('style', 'color: #606060; ' +
                            'margin-right: 300px; margin-left: 10px; ' +
                            'font-weight: bold; cursor: pointer;');
                    divn.innerHTML = '[' + linesObj[i].count + '+]';
                    divn.addEventListener('click',
                            _this.openCloseItem(id), false);
                    linesObj[i].line.link.parentNode.appendChild(divn);
                }
            }
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            var _this = this;
            /*global $, filteritems, def_filter */
            general.root.postdo = function (url) {
                /*jslint unparam: true */
                $('#itemsbody').
                    load(url, function (responseTxt, statusTxt) {
                        if (statusTxt === 'success') {
                            filteritems(def_filter);
                        }

                        _this.startInventoryPlus(_this);
                    });

                return false;
            };

            this.startInventoryPlus(this);
        }; // 2}}}
    }; // 1}}}

    /**
     * @class CountBattles {{{1
     * @constructor
     */
    var CountBattles = function () {
        /**
         * @property rez
         * @type {Object}
         */
        this.rez = {win: 0, draw: 0, loss: 0, btls: []};
        /**
         * @property divContainer
         * @type {HTMLDivElement|null}
         */
        this.divContainer = null;
        /**
         * @property reg
         * @type {Object|null}
         */
        this.reg = null;
        /**
         * @property persId
         * @type {String}
         */
        this.persId = /\?id=(\d+)/.exec(general.loc)[1];
        /**
         * @property tm
         * @type {int}
         */
        this.tm = 1000;

        /**
         * @method getBattles {{{2
         * @param   {Object}    obj
         * @return  {Boolean}
         */
        this.getBattles = function (obj) {
            var btlLogs = obj.querySelectorAll('nobr>a[href*=' +
                    '"/warlog.php?bid="]>font.greenfont'),
                i;

            for (i = 0; i < btlLogs.length; i++) {
                if (this.reg.test(btlLogs[i].innerHTML)) {
                    this.rez.btls.push(btlLogs[i].parentNode.parentNode.
                            nextElementSibling);
                } else {
                    return false;
                }
            }

            return !!btlLogs.length;
        }; // 2}}}

        /**
         * @method showResult {{{2
         */
        this.showResult = function () {
            var persLink, cls, i;

            for (i = 0; i < this.rez.btls.length; i++) {
                persLink = this.rez.btls[i].
                    querySelector('a[href$="?id=' + this.persId + '"]');

                if (persLink) {
                    // noinspection JSUnresolvedFunction
                    cls = persLink.getAttribute('class');
                    if (/red/.test(cls)) {
                        this.rez.win++;
                    } else if (/blue/.test(cls)) {
                        this.rez.loss++;
                    } else {
                        this.rez.draw++;
                    }
                }
            }

            this.divContainer.innerHTML = 'Проведено боев за текущие ' +
                'сутки: <span style="font-weight: bold;">' +
                this.rez.btls.length + ' (<span style="color: #FF0000;">' +
                this.rez.win + '</span>/<span style="color: #0000FF;">' +
                this.rez.loss + '</span>/<span style="color :#008000;">' +
                this.rez.draw + '</span>)</span>';
        }; // 2}}}

        /**
         * @method startCountBattles {{{2
         * @param   {int}   ind
         */
        this.startCountBattles = function (ind) {
            if (!ind) {
                if (this.getBattles(general.doc)) {
                    this.divContainer.innerHTML = '<span style="margin-left: ' +
                        '100px;">Загрузка <img src="' + general.imgPath +
                        'preloader.gif" alt="Загрузка" ' +
                        'title="Загрузка"></span>';
                    ind++;
                    this.startCountBattles(ind);
                } else {
                    this.showResult();
                }
            } else {
                var url = 'https://' + general.domain + '/info.warstats.php?' +
                    'id=' + this.persId + '&page_id=' + ind,
                    _this = this;

                new AjaxQuery().init(url, 'GET', null, true, function (xml) {
                    var span = general.doc.createElement('span');
                    span.innerHTML = xml.responseText;
                    // если перс в это время зашел в заявку/бой, то просто
                    // перезагрузим страницу
                    if (/игрок находится в бою/.test(span.innerHTML)) {
                        general.root.location.reload();
                        return;
                    }

                    if (_this.getBattles(span)) {
                        ind++;
                        general.root.setTimeout(function () {
                            _this.startCountBattles(ind);
                        }, _this.tm);
                    } else {
                        _this.showResult();
                    }
                }, function () {
                    general.root.setTimeout(function () {
                        _this.startCountBattles(ind);
                    }, _this.tm);
                });
            }
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            var target = general.doc.querySelector('center+br+br'),
                inBattle = /игрок находится в бою/.
                    test(general.doc.body.innerHTML),
                pageId = /page_id=(\d+)/.exec(general.loc);

            // если перс НЕ в заявке/бою и мы на первой странице протоколов
            if (target && !inBattle && !(pageId && pageId[1] !== '0') &&
                    !/\(0 всего\)/.test(general.doc.body.innerHTML)) {
                this.divContainer = general.doc.createElement('div');
                this.divContainer.setAttribute('style', 'margin-left: 10px;');
                target.parentNode.insertBefore(this.divContainer, target);

                var date = new Date(),
                    year = /20(\d+)/.exec(' ' + date.getFullYear())[1],
                    month = date.getMonth() + 1,
                    day = date.getDate();

                month = month < 10 ? '0' + month : month;
                day = day < 10 ? '0' + day : day;
                this.reg  = new RegExp(day + '\\.' + month + '\\.' + year);

                this.startCountBattles(0);
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class GbCounter {{{1
     * @constructor
     */
    var GbCounter = function () {
        /**
         * @property spanCountGB
         * @type {HTMLElement|null}
         */
        this.spanCountGB = null;
        /**
         * @property countGbNow
         * @type {int}
         */
        this.countGbNow = 0;

        /**
         * @method resetGBCounter {{{2
         */
        this.resetGBCounter = function () {
            general.setData([this.countGbNow.toString()], 13);
            this.setGBCounter();
        }; // 2}}}

        /**
         *  @method setGBCounter {{{2
         */
        this.setGBCounter = function () {
            var countGbOld = general.getData(13)[0];
            if (!countGbOld) {
                this.resetGBCounter();
                return;
            }

            // noinspection JSRemoveUnnecessaryParentheses
            var diff = this.countGbNow - (+countGbOld);
            this.spanCountGB.innerHTML = '[' +
                new SetPoints().init(diff, '.', true) + ']';
            this.spanCountGB.style.color = diff >= 0 ?
                    '#FF0000' : '#0000FF';
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            // на главной странице личного NPC
            if (/\?nid=\d+/.test(general.loc)) {
                return;
            }

            var divGB = general.doc.querySelector('td>b>div[id="cdiv"]');
            if (divGB) {
                this.countGbNow = +divGB.innerHTML.replace(/,/g, '');
                this.spanCountGB = general.doc.createElement('span');
                this.spanCountGB.setAttribute('style', 'margin-left: 5px; ' +
                        'font-weight: normal; font-size: 7pt; ' +
                        'cursor: pointer;');
                divGB.appendChild(this.spanCountGB);

                this.setGBCounter();

                var _this = this;
                this.spanCountGB.addEventListener('click', function () {
                    if (confirm('Сбросить счетчик?')) {
                        _this.resetGBCounter();
                    }
                }, false);
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class BuyHightech {{{1
     * @constructor
     */
    var BuyHightech = function () {
        /**
         * @method init {{{2
         */
        this.init = function () {
            if (/\/shopc\.php/.test(general.loc)) {
                var descrTd = general.doc.querySelectorAll('td[class$=' +
                         '"lightbg"][valign="top"][align="left"]' +
                             '[width="100%"]'),
                    strength,
                    price,
                    id,
                    i;

                for (i = 0; i < descrTd.length; i++) {
                    id = /id=(.+)$/.exec(descrTd[i].parentNode.
                            querySelector('a').href)[1];
                    price = /(\d+) EUN/.exec(descrTd[i].innerHTML)[1];
                    strength = /Прочность:<\/b> (\d+)/i.
                                    exec(descrTd[i].innerHTML)[1];

                    descrTd[i].removeChild(descrTd[i].lastElementChild);
                    descrTd[i].innerHTML += ' <span style="font-weight: ' +
                        'bold; margin-left: 7px;"> Создать объявление: ' +
                        '</span><a target="_blank" style="color: #0000FF;" ' +
                        'href="https://' + general.domain + '/market-p.php?' +
                        'stage=2&item_id=' + id + '&action_id=2&p=' + price +
                        '&s=' + strength + '">[Купить]' + '</a> ' +
                        '<a target="_blank" style="color: #990000;" href=' +
                        '"https://' + general.domain + '/market-p.php?' +
                        'stage=2&item_id=' + id + '&action_id=1&p=' + price +
                        '&s=' + strength + '">[Продать]</a>';
                }

                return;
            }

            // на странице подачи объявлений
            var param = /&p=(\d+)&s=(\d+)$/.exec(general.loc);
            if (param) {
                general.doc.querySelector('td[colspan="3"]' +
                    '[class="greenlightbg"]').innerHTML += ' <span ' +
                    'style="color: #990000;">[Стоимость в магазине: ' +
                    param[1] + ' EUN]</span>';

                //остров любой
                general.doc.querySelector('select[name="island"]').value = '-1';

                var dur1 = general.doc.
                            querySelector('input[name="durability1"]'),
                    dur2 = general.doc.
                            querySelector('input[name="durability2"]');

                //если продаем, то прочность максимальная,
                //если покупаем, то минимальная
                if (/action_id=1/.test(general.loc)) {
                    dur1.value = param[2];
                    dur2.value = param[2];
                } else {
                    dur1.value = '0';
                    dur2.value = '1';
                }

                // срок размещения 7 дней
                general.doc.
                    querySelector('select[name="date_len"]').value = '7';
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class NewsAndInvit {{{1
     * @constructor
     */
    var NewsAndInvit = function () {
        /**
         * @method isForumPage {{{2
         * @return   {Boolean}
         */
        this.isForumPage = function () {
            // noinspection JSRemoveUnnecessaryParentheses
            return (/fid=1&tid=\d+/.test(general.loc));
        }; // 2}}}

        /**
         * @method blink {{{2
         * @param   {Object}    link
         */
        this.blink = function (link) {
            link.style.color = '#FF0000';
            general.root.setInterval(function () {
                link.style.color = link.style.color ? '' : '#FF0000';
            }, 700);
        }; // 2}}}

        /**
         * @method getNewsId {{{2
         * @param    {String}   link
         * @return   {String}
         */
        this.getNewsId = function (link) {
            return (/&tid=(\d+)/.exec(link))[1];
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            var stData = general.getData(14)[0];
            stData = stData ? JSON.parse(stData) : null;

            var newsId;
            // на форуме новостей
            if (this.isForumPage() && stData) {
                newsId = this.getNewsId(general.loc);
                if (stData[newsId] === '0') {
                    stData[newsId] = '1';
                    general.setData(JSON.stringify(stData), 14);
                }

                return;
            }

            // на главной
            // приглосы
            var invit = general.doc.querySelector('a[href*="/me.php?nb=synd"]');
            if (invit) {
                invit.setAttribute('style', 'color: #FF0000; ' +
                        'font-weight: bold;');
            }

            // новости
            var newsLinks = general.doc.querySelectorAll('nobr>' +
                    'a[href*="/messages.php?fid=1&tid="]');

            // noinspection JSUnresolvedVariable
            if (newsLinks.length) {
                var newData = {},
                    i;

                for (i = 0; i < newsLinks.length; i++) {
                    newsId = this.getNewsId(newsLinks[i].href);
                    // storage пустой... записываем все темы как прочитанные
                    if (!stData) {
                        newData[newsId] = '1';
                        continue;
                    }

                    if (!stData[newsId]) {
                        stData[newsId] = '0';
                    }

                    if (stData[newsId] === '0') {
                        this.blink(newsLinks[i]);
                    }

                    newData[newsId] = stData[newsId];
                }

                general.setData(JSON.stringify(newData), 14);
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class FilterResOnStat {{{1
     * @constructor
     */
    var FilterResOnStat = function () {
        /**
         * @method delSpaces {{{2
         * @param   {String}    str
         * @return  {String}
         */
        this.delSpaces = function (str) {
            return str.replace(/^\s*/, '').replace(/\s*$/, '').
                replace(/\s,/g, ',').replace(/,\s/g, ',').
                replace(/&nbsp;/g, '').replace(/&amp;/g, '&');
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            var tbl = general.doc.querySelector('table[border="0"]' +
                    '[class="wb"]');

            if (tbl) {
                var res = this.delSpaces(general.getData(15)[0]).split(','),
                    trs = tbl.querySelectorAll('tr'),
                    nameRes,
                    i;

                for (i = 1; i < trs.length; i++) {
                    nameRes = this.delSpaces(/[^(]+/.exec(trs[i].
                                    firstElementChild.innerHTML)[0]);
                    if (res.indexOf(nameRes) === -1) {
                        trs[i].style.display = 'none';
                    }
                }
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class FilterWarlistOne2One {{{1
     * @constructor
     */
    var FilterWarlistOne2One = function () {
        /**
         * @property table
         * @type {HTMLTableElement}
         */
        this.table = general.doc.querySelector('table[cellpadding="5"]');

        /**
         * @method sortWeapon {{{2
         */
        this.sortWeapon = function () {
            var weapon = general.$('w_name').value.toLowerCase(),
                tr = this.table.querySelectorAll('tr'),
                text,
                i;

            for (i = 0; i < tr.length; i++) {
                /*eslint-disable no-useless-escape */
                if (tr[i].firstElementChild &&
                        /<b>[^\[]*\[\d+\]/.test(tr[i].innerHTML)) {
                    /*eslint-enable no-useless-escape */
                    tr[i].style.display = '';
                    if (weapon) {
                        text = tr[i].firstElementChild.nextElementSibling.
                            innerHTML.toLowerCase();
                        if (text.indexOf(weapon) === -1) {
                            tr[i].style.display = 'none';
                        }
                    }
                }
            }
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            if (/Подтверждаете бой с/.test(general.doc.body.innerHTML)) {
                return;
            }

            var filtForm = general.doc.
                    querySelector('form[action$="/warlist.php"]');

            if (filtForm && this.table) {
                filtForm = filtForm.cloneNode(false);
                filtForm.setAttribute('style', 'display: inline-block; ' +
                    'margin: 0 10px 0 10px;');

                var hidden1 = general.
                        doc.querySelector('input[name="levelset"]').
                            cloneNode(false),
                    hidden2 = general.doc.
                        querySelector('input[name="war"]').cloneNode(false),
                    s_lmin = general.doc.
                        querySelector('select[name="s_lmin"]').cloneNode(true),
                    s_lmax = general.doc.
                        querySelector('select[name="s_lmax"]').cloneNode(true),
                    s_ltype = general.doc.
                        querySelector('select[name="s_ltype"]').cloneNode(true);

                filtForm.appendChild(hidden1);
                filtForm.appendChild(hidden2);
                filtForm.appendChild(general.doc.createTextNode('от '));
                filtForm.appendChild(s_lmin);
                filtForm.appendChild(general.doc.createTextNode(' до '));
                filtForm.appendChild(s_lmax);
                filtForm.appendChild(general.doc.createTextNode(' тип '));
                filtForm.appendChild(s_ltype);
                var subm = general.doc.createElement('input');
                subm.type = 'submit';
                subm.value = '»';
                filtForm.appendChild(subm);

                var target = general.$('updatetimer').nextElementSibling;
                target.parentNode.
                    insertBefore(filtForm, target.nextElementSibling);

                s_lmin.setAttribute('style', 'width: 40px;');
                s_lmax.setAttribute('style', 'width: 40px;');

                var span = general.doc.createElement('span');
                span.innerHTML = 'Название: <input id="w_name" ' +
                    'style="width: 150px;" value="' + general.getData(16)[0] +
                    '" />';
                target.parentNode.insertBefore(span, filtForm.nextSibling);

                var _this = this;
                general.$('w_name').addEventListener('input', function () {
                    var weapName = this;
                    general.setData(weapName.value, 16);
                    _this.sortWeapon();
                }, false);

                //удаляем нижнюю форму
                this.table.firstElementChild.
                    removeChild(this.table.firstElementChild.lastElementChild);

                this.sortWeapon();
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class FixSkills {{{1
     * @constructor
     */
    var FixSkills = function () {
        /**
         * @property skills {{{2
         * @type {Array}
         */
        this.skills = [
            ['0', 4], ['1', 8], ['2', 13], ['3', 23], ['4', 36], ['5', 56],
            ['6', 84], ['7', 123], ['8', 176], ['9', 248], ['10', 344],
            ['11', 471], ['12', 637], ['13', 852], ['14', 1128], ['15', 1480],
            ['16', 1926], ['17', 2489], ['18', 3193], ['19', 4070],
            ['20', 5500], ['20/1', 7140], ['20/2', 9270], ['20/3', 12050],
            ['20/4', 15600], ['20/5', 20000], ['20/6', 26300], ['20/7', 34200],
            ['20/8', 45000], ['20/9', 58000]
        ]; // 2}}}

        /**
         * @method fixSkills {{{2
         * @param   {NodeList}  nbrs
         */
        this.fixSkills = function (nbrs) {
            var residue,
                font,
                rez,
                x,
                i,
                j;

            for (i = 0; i < nbrs.length; i++) {
                x = /\([^>]+>(\d+.?\d*)<\/span>\s?\)\s*.*\+-\d+.?\d*<\/font>/.
                    exec(nbrs[i].innerHTML);

                if (x) {
                    x = parseFloat(x[1]);

                    rez = 0;
                    for (j = 0; j < this.skills.length; j++) {
                        if (x < this.skills[j][1]) {
                            rez = this.skills[j];
                            break;
                        }
                    }

                    if (!rez) {
                        rez = ['20/10'];
                    }

                    font = nbrs[i].querySelectorAll('font');
                    font[0].innerHTML = rez[0];

                    if (rez[1]) {
                        residue = rez[1] - x;
                        //если есть знаки после запятой, то оставляем один
                        residue = residue - Math.floor(residue) ?
                                residue.toFixed(1) : residue.toFixed(0);
                        font[1].innerHTML = '+' + residue;
                    }
                }
            }
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            var nobrs, css;
            if (/\/info\.php\?id=/.test(general.loc)) {
                css = 'td[class="greenbrightbg"][align="right"]' +
                    '[valign="top"]>table:not([align="center"])';
                nobrs = general.doc.querySelector(css).querySelectorAll('nobr');
            } else {
                css = 'td[valign="top"][align="center"]>' +
                    'table[cellspacing="0"][cellpadding="0"][border="0"]';
                nobrs = general.doc.querySelector(css).querySelectorAll('nobr');
            }

            this.fixSkills(nobrs);
        }; // 2}}}
    }; // 1}}}

    /**
     * @class FuckTheFarm {{{1
     * @constructor
     */
    var FuckTheFarm = function () {
        /**
         * @method init {{{2
         */
        this.init = function () {
            var link;

            if (/\/me(\/|\.php)/.test(general.loc)) {
                link = general.doc.querySelector('[src$="images.' +
                    general.domain.replace('www.', '') +
                    '/i/home/farm.gif"]').parentNode;
                var lparent = link.parentNode;
                lparent.removeChild(link.previousElementSibling);
                lparent.removeChild(link.nextElementSibling);
                lparent.removeChild(link);
                return;
            }

            link = general.doc.
                querySelector('a[href*="/info.ach.php?id="]+' +
                    'a[href*="/info.ach.php?id="]').nextSibling;
            while (link.nextSibling) {
                link.parentNode.removeChild(link.nextSibling);
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class HistorySms {{{1
     * @constructor
     */
    var HistorySms = function () {
        /**
         * @property nik
         * @type {String|null}
         */
        this.nik = null;
        /**
         * @property uncodeNik
         * @type {String|null}
         */
        this.uncodeNik = null;

        /**
         * @property spanContent
         * @type {Object}
         */
        this.spanContent = general.doc.createElement('span');
        /**
         * @property sms
         * @type {Array}
         */
        this.sms = [];
        /**
         * @property numberSms
         * @type {int}
         */
        this.numberSms = 3;

        // noinspection FunctionWithInconsistentReturnsJS
        /**
         * @method sortMess {{{2
         * @return   {int}
         */
        this.sortMess = function () {
            this.sms.sort(function (a, b) {
                var ret;

                if (a.id < b.id) {
                    ret = 1;
                } else if (a.id > b.id) {
                    ret = -1;
                } else {
                    ret = 0;
                }

                return ret;
            });
        }; // 2}}}

        /**
         * @method createDivSms {{{2
         * @return   {Object}
         */
        this.createDivSms = function () {
            var target = general.doc.
                    querySelector('div[style*="overflow:auto"]'),
                divSms = general.doc.createElement('div');

            divSms.setAttribute('name', 'historySms');
            divSms.setAttribute('style', 'font-size: 8pt; margin-bottom: 7px;');
            target.insertBefore(divSms, target.firstChild);

            return divSms;
        }; // 2}}}

        /**
         * @method showOfSyndLinks {{{2
         */
        this.showOfSyndLinks = function () {
            var mass = [],
                i;

            for (i = 0; i < this.sms.length; i++) {
                mass.push(this.sms[i].ofSyndLink);
            }

            this.createDivSms().innerHTML = '<span style="color: #FF0000;">' +
                'Ссылки на сообщения, доступные для официальных синдикатов:' +
                '</span><div style="color: #000000;">' +
                mass.reverse().join('<br>') + '</div><hr>';
            general.$('showOfSyndLinks').style.display = 'none';
        }; // 2}}}

        /**
         * @method showSms {{{2
         */
        this.showSms = function () {
            var i;

            for (i = 0; i < this.sms.length; i++) {
                this.createDivSms().innerHTML = this.sms[i].mess +
                    (!i ? '<hr>' : '');
            }

            general.$('showOfSyndLinks').style.display = '';
            general.$('counter').innerHTML = '';
            general.$('preloader').style.display = 'none';
            general.$('showHistory').removeAttribute('on');
        }; // 2}}}

        /**
         * @method nextAjaxQuery {{{2
         * @param    {Boolean}  outbox
         */
        this.nextAjaxQuery = function (outbox) {
            var _this = this;

            general.root.setTimeout(function () {
                _this.showHistory(outbox ? 0 : 1, 0, outbox ? null : 1);
            }, 1000);
        }; // 2}}}

        /**
         * @method showHistory {{{2
         * @param   {int}       id
         * @param   {int}       id1
         * @param   {int|null}  id2
         */
        this.showHistory = function (id, id1, id2) {
            var counter = general.$('counter'),
                url;

            // noinspection JSIncompatibleTypesComparison
            if (id2 !== null) {
                counter.innerHTML = !id ? 'входящие...' : 'исходящие...';
                url = 'https://' + general.domain + '/sms.php?page=' + id +
                    '&page_id1=' + (!id ? id1 : 0) + '&page_id2=' +
                    (id ? id1 : 0) + '&search=' + this.uncodeNik;
            } else {
                if (!id) {
                    this.sortMess();
                }

                counter.innerHTML = (id + 1).toString() + '/' + this.sms.length;
                url = this.sms[id].href;
            }

            var _this = this;
            new AjaxQuery().init(url, 'GET', null, true, function (xml) {
                _this.spanContent.innerHTML = xml.responseText;

                // noinspection JSIncompatibleTypesComparison
                if (id2 !== null) {
                    var linksSms = _this.spanContent.
                            querySelectorAll('td[width="100%"]>' +
                                'a[href*="/sms-read.php?type="]'),
                        outbox = id2 === 1;

                    if (!linksSms.length) {
                        _this.nextAjaxQuery(outbox);
                        return;
                    }

                    var color = '<span style="font-weight: bold; color: #' +
                                    (outbox ? '008000' : 'AC4311') + ';">',
                        allCountSms = !outbox ?
                                _this.numberSms : _this.numberSms * 2,
                        i;

                    for (i = 0; i < linksSms.length; i++) {
                        _this.sms.push({
                            id: +/&id=(\d+)/.exec(linksSms[i].href)[1],
                            href: linksSms[i].href,
                            mess: color + (outbox ? 'Я' : _this.nik),
                            ofSyndLink: ''
                        });

                        // noinspection JSRemoveUnnecessaryParentheses
                        if (allCountSms === _this.sms.length ||
                                // последняя ссылка на странице,
                                // но ссылок меньше 30 => больше страниц нет
                                // (по 30 сообщений на одной странице)
                                (i === linksSms.length - 1 &&
                                    linksSms.length < 30)) {
                            _this.nextAjaxQuery(outbox);

                            return;
                        }
                    }

                    general.root.setTimeout(function () {
                        id1++;
                        _this.showHistory(id, id1, id2);
                    }, 1000);
                } else {
                    var time = _this.spanContent.querySelector('td[width=' +
                            '"100%"]>a[href*="/info.php?id="]:first-child').
                                nextSibling.nodeValue;

                    _this.sms[id].mess += time + ':</span><br>' +
                        _this.spanContent.querySelector('div[style*=' +
                                '"overflow:auto"]').innerHTML.
                                    replace(/^(\s|<br>)*|(\s|<br>)*$/g, '');
                    _this.sms[id].ofSyndLink = _this.spanContent.
                        querySelector('#attndiv+center>' +
                            'a[href*="/sms-pub.php?type="]').href;

                    id++;
                    if (!_this.sms[id]) {
                        _this.showSms();
                        return;
                    }

                    general.root.setTimeout(function () {
                        _this.showHistory(id, 0, null);
                    }, 1000);
                }
            }, function () {
                general.root.setTimeout(function () {
                    _this.showHistory(id, id1, id2);
                }, 1000);
            });
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            var target = general.doc.querySelector('td[width="100%"]>' +
                    'a[href*="/info.php?id="]:first-child'),
                _this = this;

            target.parentNode.innerHTML += '<span id="showHistory" ' +
                'style="color: #008000; cursor: pointer; margin-left: 10px;">' +
                'История</span> <input id="countSms" maxlength="5" ' +
                'style="width: 45px;" value="' + this.numberSms +
                '" title="Количество исходящих сообщений" /> ' +
                '<span id="preloader" style="margin: 0 10px 0 10px; ' +
                'display: none;"><img src="' + general.imgPath +
                'preloader.gif" alt="img" /></span><span id="counter"></span>' +
                '<span id="showOfSyndLinks" style="cursor: pointer; ' +
                'display: none; text-decoration: underline;">ссылки</span>';

            general.$('showOfSyndLinks').addEventListener('click', function () {
                _this.showOfSyndLinks();
            }, false);

            general.$('countSms').addEventListener('keypress', function (e) {
                var ev = e || general.root.event;
                if (ev.keyCode === 13) {
                    general.$('showHistory').click();
                }
            }, false);

            this.nik = target.querySelector('b').innerHTML;
            this.uncodeNik = new UrlEncode().init(this.nik);

            general.$('showHistory').addEventListener('click', function () {
                var lnk = this;
                if (lnk.getAttribute('on')) {
                    return;
                }

                _this.numberSms = +general.$('countSms').value;
                if (isNaN(_this.numberSms) || _this.numberSms < 1) {
                    alert('Не верно введено количество исходящих сообщений');
                    return;
                }

                lnk.setAttribute('on', '1');
                // удаляем уже выведенные сообщения
                var divHistory = general.doc.
                        querySelectorAll('div[name="historySms"]'),
                    i;

                for (i = 0; i < divHistory.length; i++) {
                    divHistory[i].parentNode.removeChild(divHistory[i]);
                }

                _this.sms = [];
                general.$('preloader').style.display = '';
                general.$('showOfSyndLinks').style.display = 'none';
                _this.showHistory(0, 0, 0);
            }, false);
        }; // 2}}}
    }; // 1}}}

    /**
     * @class LinksToHighTech {{{1
     * @constructor
     */
    var LinksToHighTech = function () {
        /**
         * @property highTechItems {{{2
         * @type {Object}
         */
        this.highTechItems = {
            'htGroup': ['auto', 'heavy', 'sniper', 'ppguns', 'shotguns',
                'grl', 'armour', 'helmets', 'boots', 'masks', 'wear',
                'belts', 'rightpocket', 'leftpocket', 'epockets', 'drugs',
                'transport'],
            'sniper': 'snipe'
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            var links = general.doc.
                    querySelector('td[valign="top"][width="200"]').
                        querySelectorAll('a[href*="/shop.php?shop=shop_"]'),
                group,
                i;

            for (i = 0; i < links.length; i++) {
                if (links[i].innerHTML) {
                    group = /\?shop=shop_(.*)$/.exec(links[i].href)[1];
                    if (this.highTechItems.htGroup.indexOf(group) !== -1) {
                        // noinspection JSUnresolvedVariable
                        links[i].parentNode.innerHTML = '<a ' +
                            'href="/shopc.php?shop=shop_' +
                            (this.highTechItems[group] || group) +
                            '_c" style="color: #AC4311; margin-right: 5px;">' +
                            '[Ht]</a>' + links[i].parentNode.innerHTML;
                    }
                }
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class GameMania {{{1
     * @constructor
     */
    var GameMania = function () {
        /**
         * @property target
         * @type {HTMLTableCellElement}
         */
        this.target = general.doc.querySelector('td[class="greenbrightbg"]' +
            '[valign="top"][align="left"]');
        // noinspection JSUnusedGlobalSymbols
        /**
         * @property total
         * @type {int}
         */
        this.total = 0;

        /**
         * @method calc {{{2
         * @param   {Object}    reg1
         * @param   {Object}    reg2
         * @return  {int}
         */
        this.calc = function (reg1, reg2) {
            var spent = reg1.test(this.target.innerHTML) ?
                        +reg1.exec(this.target.innerHTML)[1].
                            replace(/,/g, '') : 0,
                prize = reg2.test(this.target.innerHTML) ?
                        +reg2.exec(this.target.innerHTML)[1].
                            replace(/,/g, '') : 0,
                rez = prize - spent;

            if (!rez) {
                return 0;
            }

            // noinspection JSUnusedGlobalSymbols
            this.total += rez;
            return rez;
        }; // 2}}}

        /**
         * @method getStrGameRez {{{2
         * @param   {int}       rez
         * @param   {String}    game
         * @param   {Boolean}   ttl
         * @return  {String}
         */
        this.getStrGameRez = function (rez, game, ttl) {
            return '<tr><td style="' +
                (!ttl ? 'color: #008000' : 'font-weight: bold') + ';">' +
                game + ':</td>' + '<td style="color: ' +
                (rez < 0 ? '#0000FF' : '#FF0000') + ';">$' +
                new SetPoints().init(rez, ',', false) + '</td></tr>';
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            if (!this.target ||
                    !/Отработано часов/.test(this.target.innerHTML)) {
                return;
            }

            var roul = this.calc(/Потрачено в казино: <b>\$([^<]*)/i,
                    /Выигрыш в казино: <b>\$([^<]*)/i),
                tot = this.calc(/Потрачено в тотализаторе: <b>\$([^<]*)/i,
                    /Выигрыш в тотализаторе: <b>\$([^<]*)/i),
                fight = /Выигрыш в боях/i.test(this.target.innerHTML);

            if ((roul || tot || fight)) {
                if (fight) {
                    this.total += +/Выигрыш в боях: <b>\$([^<]*)/i.
                            exec(this.target.innerHTML)[1].replace(/,/g, '');
                }

                var str = '<hr><table>';
                if (roul) {
                    str += this.getStrGameRez(roul, 'Рулетка', false);
                }

                if (tot) {
                    str += this.getStrGameRez(tot, 'Тотализатор', false);
                }

                str += this.getStrGameRez(this.total, 'Всего', true);

                if (fight) {
                    str += '<tr><td colspan="2" style="font-size: 10px;">' +
                        '(с учетом выигрыша в боях)</td></tr>';
                }

                str += '</table>';

                var div = general.doc.createElement('div');
                div.innerHTML = str;
                this.target.appendChild(div);
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class GosEnergoAtomFilter {{{1
     * @constructor
     */
    var GosEnergoAtomFilter = function () {
        /**
         * @property trs
         * @type {NodeList|null}
         */
        this.trs = null;

        /**
         * @method sortGosEnergoAtomBySynd {{{2
         */
        this.sortGosEnergoAtomBySynd = function () {
            var prnt = this.trs[0].parentNode,
                objs = {},
                syndID,
                i;

            for (i = 1; i < this.trs.length; i++) {
                syndID = /\/syndicate\.php\?id=(\d+)/.
                    exec(this.trs[i].innerHTML);
                syndID = syndID ? syndID[1] : 0;
                if (!objs[syndID]) {
                    objs[syndID] = [];
                }

                objs[syndID].push(this.trs[i].cloneNode(true));
                prnt.removeChild(this.trs[i]);
            }

            var synd, opt;
            for (synd in objs) {
                if (objs.hasOwnProperty(synd)) {
                    for (i = 0; i < objs[synd].length; i++) {
                        prnt.appendChild(objs[synd][i]);
                    }

                    opt = general.doc.createElement('option');
                    opt.value = synd;
                    opt.innerHTML = +synd ? '#' + synd : 'Ничейки';
                    general.$('selectSynd').appendChild(opt);
                }
            }

            this.getTrsTable();
        }; // 2}}}

        /**
         * @method resetGosEnergoAtom {{{2
         */
        this.resetGosEnergoAtom = function () {
            var i;

            for (i = 1; i < this.trs.length; i++) {
                this.trs[i].style.display = '';
            }
        }; // 2}}}

        /**
         * @method sortGosEnergoAtom {{{2
         */
        this.sortGosEnergoAtom = function () {
            var stData = general.getData(17),
                val1 = stData[0],
                val2 = stData[1],
                val3 = stData[2],
                i;

            this.resetGosEnergoAtom();
            if (!val1 && !val2 && !val3) {
                return;
            }

            switch (val2) {
            case '1':
                val2 = 'Электростанция';
                break;

            case '2':
                val2 = 'Урановый рудник';
                break;

            case '3':
                val2 = 'Урановый рудник [2]';
                break;

            case '4':
                val2 = 'Урановый рудник [3]';
                break;

            default:
                val2 = false;
                break;
            }

            for (i = 1; i < this.trs.length; i++) {
                if (val1 && this.trs[i].innerHTML.
                        indexOf('[' + val1 + ']') === -1) {
                    this.trs[i].style.display = 'none';
                }

                if (val2 && this.trs[i].innerText.indexOf(val2) === -1) {
                    this.trs[i].style.display = 'none';
                }

                if (val3) {
                    // noinspection JSRemoveUnnecessaryParentheses
                    if ((val3 === '0' && /\/syndicate\.php\?id=\d+/.
                            test(this.trs[i].innerHTML)) ||
                                (+val3 && this.trs[i].innerHTML.
                                    indexOf('/img/synds/' + val3 +
                                        '.gif') === -1)) {
                        this.trs[i].style.display = 'none';
                    }
                }
            }
        }; // 2}}}

        /**
         * @method selectChangeHandler {{{2
         * @param    {Object}   sel
         */
        this.selectChangeHandler = function (sel) {
            var stData = general.getData(17),
                stInd = sel.id === 'selectIsland' ? 0 :
                            (sel.id === 'selectObject' ? 1 : 2),
                val = sel.value;

            stData[stInd] = stInd !== 2 ? (val === '0' ? '' : val) :
                    (val === 'all' ? '' : val);
            general.setData(stData, 17);
            this.sortGosEnergoAtom();
        }; // 2}}}

        // noinspection FunctionWithInconsistentReturnsJS
        /**
         * @method getTrsTable {{{2
         * @return  {Array}
         */
        this.getTrsTable = function () {
            this.trs = general.doc.querySelector('table.withborders' +
                    '[cellpadding="5"][cellspacing="1"]').
                            querySelectorAll('tr');
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            var divSort = general.doc.createElement('div');
            divSort.setAttribute('style', 'position: absolute; top: 100px; ' +
                    'left: 20px;');
            divSort.innerHTML = '<table><tr><td>Остров:</td>' +
                '<td><select id="selectIsland"><option value="0">Все</option>' +
                '<option value="Z">[Z]</option><option value="G">[G]</option>' +
                '<option value="S">[S]</option></select></td></tr>' +
                '<tr><td>Объект:</td><td><select id="selectObject" ' +
                'style="margin-top: 5px;"><option value="0">Все</option>' +
                '<option value="1">ЭС</option><option value="2">Уран</option>' +
                '<option value="3">Уран [2]</option><option value="4">' +
                'Уран [3]</option></select></td></tr><tr><td>Синдикат:</td>' +
                '<td><select id="selectSynd" style="margin-top: 5px;">' +
                '<option value="all">Все</option></select></td></tr></table>';
            general.doc.body.appendChild(divSort);

            var stData = general.getData(17),
                _this = this;

            var isl = general.$('selectIsland');
            isl.addEventListener('change', function () {
                _this.selectChangeHandler(this);
            }, false);

            var obj = general.$('selectObject');
            obj.addEventListener('change', function () {
                _this.selectChangeHandler(this);
            }, false);

            var synd = general.$('selectSynd');
            synd.addEventListener('change', function () {
                _this.selectChangeHandler(this);
            }, false);

            this.getTrsTable();
            this.sortGosEnergoAtomBySynd();

            isl.value = stData[0] || '0';
            obj.value = stData[1] || '0';
            synd.value = stData[2] || 'all';
            this.sortGosEnergoAtom();
        }; // 2}}}
    }; // 1}}}

    /**
     * @class SortSyndOnline {{{1
     * @constructor
     */
    var SortSyndOnline = function () {
        /**
         * @property mainTable
         * @type {Object|null}
         */
        this.mainTable = general.doc.querySelector('table[width="600"]' +
                '[align="center"][cellspacing="0"][cellpadding="0"]');

        /**
         * @method sortBattles {{{2
         */
        this.sortBattles = function () {
            var trs = this.mainTable.querySelectorAll('tr'),
                reg = /\/warlog\.php\?bid=(\d+)/,
                battles = {},
                bid,
                i;

            for (i = 1; i < trs.length; i++) {
                bid = reg.exec(trs[i].innerHTML);
                // alert(trs[i].innerHTML);
                if (/<b>(\s?|&nbsp;)?\d+(\s?|&nbsp;)?<\/b>$/.
                        test(trs[i].firstElementChild.innerHTML) &&
                            bid) {
                    if (!battles[bid[1]]) {
                        battles[bid[1]] = [];
                    }

                    battles[bid[1]].push(trs[i].cloneNode(true));
                    trs[i].parentNode.removeChild(trs[i]);
                }
            }

            var target = this.mainTable.querySelector('tr'),
                tr = general.doc.createElement('tr');

            tr.innerHTML = '<td><table class="bordersupdown" width="100%" ' +
                'cellspacing="1" cellpadding="4" align="center" ' +
                'style="margin-bottom: 15px;"><tbody></tbody></table></td>';
            target.parentNode.insertBefore(tr, target);
            target = tr.querySelector('tbody');

            // ссылки на идущие синдикатные бои (таблица внизу страницы)
            var syndBattles = general.doc.
                    querySelectorAll('td[class="greengreenbg"][valign="top"]>' +
                        'a[href*="/warlog.php?bid="]'),
                countBattles = 1,
                color,
                btl;

            for (btl in battles) {
                if (battles.hasOwnProperty(btl)) {
                    color = '';
                    for (i = 0; i < syndBattles.length; i++) {
                        // если бой синдикатный, выделяем зеленым цветом
                        if (btl === reg.exec(syndBattles[i].href)[1]) {
                            color = ' style="color: #00AA00;"';
                            break;
                        }
                    }

                    tr = general.doc.createElement('tr');
                    tr.innerHTML = '<td colspan="8" class="greenbg" ' +
                        'style="text-align: center; font-weight: bold;">' +
                        '<span' + color + '>Бой: ' + countBattles +
                        '</span></td>';
                    target.appendChild(tr);

                    for (i = 0; i < battles[btl].length; i++) {
                        target.appendChild(battles[btl][i]);
                    }

                    countBattles++;
                }
            }
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            if (/&page=online$/.test(general.loc) && this.mainTable &&
                    this.mainTable.
                        querySelector('td>a[href*="/warlog.php?bid="]>' +
                            'img[src*="/i/icons/"]')) {
                this.sortBattles();
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class HousHealth {{{1
     * @constructor
     */
    var HousHealth = function () {
        /**
         * @method showSector {{{2
         * @param   {String}    url
         * @param   {string}    sector
         */
        this.showSector = function (url, sector) {
            var _this = this;

            new AjaxQuery().init(url, 'GET', null, true, function (xml) {
                var spanContent = general.doc.createElement('span'),
                    cssSelector;

                spanContent.innerHTML = xml.responseText;

                if (!sector) {    // ищем сектор перса
                    cssSelector = 'b+a[href*="/map.php?s"]';
                    sector = spanContent.querySelector(cssSelector).innerHTML;
                    general.root.setTimeout(function () {
                        // на недвижимость перса
                        _this.showSector('https://' + general.domain +
                            '/info.realty.php?id=' + general.myID, sector);
                    }, 1000);
                } else {
                    cssSelector = 'table.withborders';
                    var table = spanContent.querySelector(cssSelector);

                    if (table) {
                        var trs = table.querySelectorAll('tr'),
                            td,
                            i;

                        for (i = 1; i < trs.length; i++) {
                            td = trs[i].firstElementChild;
                            if (/Частный дом/.test(td.innerHTML) &&
                                    td.nextElementSibling.innerHTML.
                                        indexOf(sector) !== -1) {
                                return;
                            }
                        }
                    }

                    alert('Вы находитесь не в секторе с домиком !');
                }
            }, function () {
                general.root.setTimeout(function () {
                    _this.showSector(url, sector);
                }, 1000);
            });
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            if (/b0/.test(general.loc)) {
                general.setData('1', 19);
                return;
            }

            if (general.getData(19)[0]) {
                general.setData('', 19);

                // если здоровье менее 80%
                if (general.doc.querySelector('#hpheader>font')) {
                    this.showSector('https://' + general.domain +
                        '/info.php?id=' + general.myID, '');
                }
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class LinksInOne2One {{{1
     * @constructor
     */
    var LinksInOne2One = function () {
        /**
         * @method init {{{2
         */
        this.init = function () {
            var table = general.doc.querySelector('td[class="txt"]>' +
                    'table[border="0"][cellpadding="5"][cellspacing="1"]');

            if (table) {
                var trs = table.querySelectorAll('tr'),
                    last,
                    name,
                    i;

                for (i = 0; i < trs.length; i++) {
                    last = trs[i].lastElementChild;
                    name = /вызван (.*)( \[\d+\])/.exec(last.innerHTML);
                    if (name) {
                        last.innerHTML = 'вызван <a target="_blank" ' +
                            'href="https://' + general.domain +
                            '/search.php?key=' + name[1] +
                            '" style="text-decoration: none; ' +
                            'font-weight: bold;">' + name[1] + '</a>' + name[2];
                    }
                }
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class One2OneCallerInfo {{{1
     * @constructor
     */
    var One2OneCallerInfo = function () {
        /**
         * @property weapon
         * @type {Node|null}
         */
        this.weapon = null;
        /**
         * @property twoHand
         * @type {Boolean}
         */
        this.twoHand = false;

        /**
         * @method getRange {{{2
         * @param   {int}       ind
         * @param   {string}    str
         */
        this.getRange = function (ind, str) {
            var _this = this,
                a = _this.weapon.querySelectorAll('a'),
                url = a[ind].href,
                range;

            new AjaxQuery().init(url, 'GET', null, true, function (xml) {
                range = /стрельбы: (\d+) ходов/i.exec(xml.responseText);
                str += range ? range[1] : 'не найдена';

                if (!_this.twoHand || ind || a[1].href === url) {
                    var div = general.doc.createElement('div');
                    div.setAttribute('style', 'color: #0000FF; ' +
                        'font-weight: bold;');
                    div.innerHTML = str;
                    _this.weapon.appendChild(div);
                } else {
                    str += ', ';
                    general.root.setTimeout(function () {
                        _this.getRange(1, str);
                    }, 1000);
                }
            }, function () {
                general.root.setTimeout(function () {
                    _this.getRange(ind, str);
                }, 1000);
            });
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            var called = general.doc.querySelector('td[class="greengreenbg"]' +
                    '[colspan="2"]>center>b>a[href*="/info.php?id="]');

            if (!called) {
                return;
            }

            general.doc.title = 'БОЙ !!!';
            new PlaySound().init(general.getData(21)[0]);

            var url = called.href,
                _this = this;

            new AjaxQuery().init(url, 'GET', null, true, function (xml) {
                var spanContent = general.doc.createElement('span'),
                    spanHp = general.doc.createElement('span');

                spanContent.innerHTML = xml.responseText;
                // здоровье
                spanHp.innerHTML = /\[(\d+) \/ (\d+)\]/.exec(spanContent.
                    querySelector('td[style="padding-top:3px;"]').innerHTML)[0];
                spanHp.setAttribute('style', 'color: #990000; ' +
                    'margin-left: 5px;');
                called.parentNode.appendChild(spanHp);

                // узел td с изображениями на оружие
                _this.weapon = spanContent.
                    querySelector('table[style="margin-right:1px;"]').
                        parentNode;
                general.doc.querySelector('table[style="padding-top:10px;"]' +
                    '[border="0"][cellspacing="1"][cellpadding="5"]' +
                        '[width="450"]').removeAttribute('width');
                _this.weapon.removeAttribute('rowspan');
                _this.weapon.firstElementChild.removeAttribute('align');
                var target = general.doc.
                        querySelector('td[class="greengreenbg"][colspan="2"]');
                target.parentNode.appendChild(_this.weapon);

                // раскрашиваем умелку
                target.innerHTML = target.innerHTML.replace(/(\(\d+\))/g,
                    '<span style="color: #00C000;">$1</span>');

                // две руки
                _this.twoHand = /Левая/.test(target.innerHTML) &&
                    /Правая/.test(target.innerHTML);

                // узел td со списком умелок
                var cssSelector = 'tr>td+td[align="right"][valign="top"]',
                    skills = spanContent.querySelectorAll(cssSelector)[2];
                skills.setAttribute('colspan', '2');
                var tr = general.doc.createElement('tr');
                tr.appendChild(skills);
                target = target.parentNode.parentNode;
                target.insertBefore(tr, target.lastElementChild);

                // узел td со списком бонусов
                var bonuses = spanContent.
                        querySelectorAll('td[align="center"][valign="top"]')[2];
                tr.appendChild(bonuses);

                // дальность оружия
                general.root.setTimeout(function () {
                    _this.getRange(0, 'Дальность оружия: ');
                }, 1000);
            }, function () {
                general.cons.log('Error xhr on One2OneCallerInfo');
            });
        }; // 2}}}
    }; // 1}}}

    /**
     * @class PortsAndTerminals {{{1
     * @constructor
     */
    var PortsAndTerminals = function () {
        /**
         * @property sectors {{{2
         * @type {Array}
         */
        this.sectors = [
            '50&sy=47|', '47&sy=49|', '49&sy=49|1', '51&sy=49|1',
            '53&sy=49|1', '48&sy=50|1', '50&sy=50|1', '52&sy=50|2',
            '49&sy=51|1', '53&sy=51|1', '47&sy=52|', '50&sy=52|1',
            '48&sy=53|1', '49&sy=53|', '53&sy=53|', '152&sy=148|',
            '149&sy=149|', '152&sy=149|1', '150&sy=150|1', '151&sy=150|',
            '149&sy=152|', '151&sy=152|2'
        ]; // 2}}}
        /**
         * @property imgPath
         * @type {String}
         */
        this.imgPath = general.imgPath + 'PortsAndTerminals/';

        /**
         * @method  createDiv
         * @param   {Object}   pos
         * @param   {String}   img
         * @param   {int}      shiftX
         */
        this.createDiv = function (pos, img, shiftX) {
            var div = general.doc.createElement('div');
            div.setAttribute('style', 'position: absolute; z-index: 999;');
            div.style.left = pos.x + shiftX;
            div.style.top = pos.y + 3;
            div.innerHTML = '<img src="' + this.imgPath + img +
                '" alt="img" />';
            general.doc.body.appendChild(div);
        };

        /**
         * @method init {{{2
         */
        this.init = function () {
            var cells = general.doc.
                    querySelectorAll('a[href*="/map.php?sx="]>img'),
                getPos = new GetPos().init,
                coord,
                tmp,
                j,
                i;

            for (i = 0; i < cells.length; i++) {
                // noinspection JSUnresolvedVariable
                coord = /\d+&sy=\d+/.exec(cells[i].parentNode.href)[0];
                for (j = 0; j < this.sectors.length; j++) {
                    tmp = this.sectors[j].split('|');
                    if (coord === tmp[0]) {
                        if (!tmp[1]) {
                            // порт
                            this.createDiv(getPos(cells[i]), 'anchor.png', 3);
                        } else if (tmp[1] === '1') {
                            // терминал
                            this.createDiv(getPos(cells[i]), 'coins.png', 3);
                        } else {
                            // порт + терминал
                            this.createDiv(getPos(cells[i]), 'anchor.png', 3);
                            this.createDiv(getPos(cells[i]), 'coins.png', 22);
                        }
                    }
                }
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class RangeWeapon {{{1
     * @constructor
     */
    var RangeWeapon = function () {
        /**
         * @property equipment
         * @type {HTMLTableElement}
         */
        this.equipment = general.doc.querySelector('td[colspan="2"]>' +
                'table[border="0"][cellspacing="0"][cellpadding="0"]');
        /**
         * @property weapon
         * @type {Array|null}
         */
        this.weapon = null;
        /**
         * @property range
         * @type {Array}
         */
        this.range = [];

        /**
         * @method setRange {{{2
         * @param   {Object}    target
         * @param   {int}       ind
         */
        this.setRange = function (target, ind) {
            var targt = target[ind].nodeName === 'B' ?
                            target[ind] : target[ind].parentNode;
            targt.innerHTML += '<span style="color: #0000FF; ' +
                    'margin-left: 5px; font-weight: bold;">[' +
                    this.range[ind] + ']</span>';
        }; // 2}}}

        /**
         * @method showRange {{{2
         */
        this.showRange = function () {
            var a  = this.equipment.
                        querySelectorAll('a[style*="font-weight:bold;"]'),
                b = this.equipment.querySelectorAll('b'),
                target = a.length ? a : b,
                i;

            for (i = 0; i < this.range.length; i++) {
                this.setRange(target, i);
            }
        }; // 2}}}

        /**
         * @method getRange {{{2
         * @param   {int}   ind
         */
        this.getRange = function (ind) {
            var url = this.weapon[ind],
                _this = this;

            new AjaxQuery().init(url, 'GET', null, true, function (xml) {
                var reg = /Дальность стрельбы: (\d+) ход/i;

                _this.range.push(reg.test(xml.responseText) ?
                                    reg.exec(xml.responseText)[1] : '-');

                ind++;
                if (_this.weapon[ind] &&
                        // в правой и левой руке разное оружие
                        _this.weapon[ind - 1] !== _this.weapon[ind]) {
                    general.root.setTimeout(function () {
                        _this.getRange(ind);
                    }, 1000);
                } else {
                    if (_this.weapon[ind]) {
                        _this.range.push(_this.range[0]);
                    }
                    _this.showRange();
                }

            }, function () {
                general.root.setTimeout(function () {
                    _this.getRange(ind);
                }, 1000);
            });
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            if (this.equipment &&
                    /(Левая|Правая) рука/.test(this.equipment.innerHTML)) {
                var css = 'td[valign="top"]>a[href*="/item.php?item_id="]',
                    txt = this.equipment.innerHTML;

                this.weapon = this.equipment.querySelectorAll(css);
                if (/Левая/.test(txt) && /Правая/.test(txt)) {
                    this.weapon = [this.weapon[0].href, this.weapon[1].href];
                } else {
                    this.weapon = [this.weapon[0].href];
                }

                this.getRange(0);
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class RentAndSale {{{1
     * @constructor
     */
    var RentAndSale = function () {
        /**
         * @method changeColor {{{2
         */
        this.changeColor = function () {
            var td = general.doc.querySelector('td[valign="top"]+' +
                    'td.greengraybg[align="left"]');

            if (td) {
                var _this = this,
                    color = _this.id === 'donotsend' ? '#E0EEE0' :
                                _this.id === 'send1' ? '#FB8F8F' : '#95CCF6';

                td.style.background = color;
                td.previousElementSibling.style.background = color;
            }
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            var radio = general.doc.querySelectorAll('input[name="sendtype"]');

            if (radio.length) {
                var scrpt = general.doc.createElement('script');
                scrpt.innerHTML = 'function checkPrice(){if(document.' +
                        'getElementById("for_money_id").value=="0"){' +
                        'if(!confirm("Указана цена 0 Гб !!! Продолжить?"))' +
                        'return false;}return true;}';
                general.doc.querySelector('head').appendChild(scrpt);

                general.doc.querySelector('form[action="/home.senditem.php"]').
                    setAttribute('onsubmit', 'return checkPrice();');

                var i;
                for (i = 0; i < radio.length; i++) {
                    radio[i].addEventListener('click', this.changeColor, false);
                }
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class ScanKarma {{{1
     * @constructor
     */
    var ScanKarma = function () {
        /**
         * @method init {{{2
         */
        this.init = function () {
            if (/\?id=(\d+)/.exec(general.loc)[1] === general.myID) {
                var karma = /Карма:\s\d+\.?\d*\s\((\d+\/\d+)\)/i.
                        exec(general.doc.body.textContent);

                if (karma) {
                    karma = karma[1];
                    if (!general.getData(23)[0]) {
                        general.setData(karma, 23);
                        return;
                    }

                    var oldKarma = general.getData(23)[0];
                    if (karma === oldKarma) {
                        return;
                    }

                    general.setData(karma, 23);

                    oldKarma = oldKarma.split('/');
                    oldKarma[0] = +oldKarma[0];
                    oldKarma[1] = +oldKarma[1];

                    karma = karma.split('/');
                    karma[0] = +karma[0];
                    karma[1] = +karma[1];

                    var str = 'Ваша карма была изменена \n\n';
                    if (karma[0] > oldKarma[0]) {
                        str += 'Отрицательная карма увеличилась на ' +
                            (karma[0] - oldKarma[0]) + ' (' + oldKarma[0] +
                            ' ---> ' + karma[0] + ')\n';
                    } else if (karma[0] < oldKarma[0]) {
                        str += 'Отрицательная карма уменьшилась на ' +
                            (oldKarma[0] - karma[0]) + ' (' + oldKarma[0] +
                            ' ---> ' + karma[0] + ')\n';
                    }

                    if (karma[1] > oldKarma[1]) {
                        str += 'Положительная карма увеличилась на ' +
                            (karma[1] - oldKarma[1]) +  ' (' + oldKarma[1] +
                            ' ---> ' + karma[1] + ')';
                    } else if (karma[1] < oldKarma[1]) {
                        str += 'Положительная карма уменьшилась на ' +
                            (oldKarma[1] - karma[1]) +  ' (' + oldKarma[1] +
                            ' ---> ' + karma[1] + ')';
                    }

                    alert(str);
                }
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class ScanPers {{{1
     * @constructor
     */
    var ScanPers = function () {
        /**
         * @property interval
         * @type {int}
         */
        this.interval = 7;
        /**
         * @property spanContent
         * @type {Element|null}
         */
        this.spanContent = general.doc.createElement('span');
        /**
         * @property ajax
         * @type {Function}
         */
        this.ajax = new AjaxQuery().init;
        /**
         * @property playSound
         * @type {Function}
         */
        this.playSound = new PlaySound().init;

        /**
         * @method showSettings {{{2
         */
        this.showSettings = function () {
            return function () {
                var settings = general.$('spSettings'),
                    vis = settings.style.visibility,
                    pos = new GetPos().init(this);

                settings.style.top = (pos.y + 25).toString();
                settings.style.left = (pos.x - 80).toString();
                settings.style.visibility = vis === 'hidden' ?
                        'visible' : 'hidden';
            };
        }; // 2}}}

        /**
         * @method changeSelect {{{2
         * @param   {int}   ind
         */
        this.changeSelect = function (ind) {
            return function () {
                var stData = general.getData(24);
                stData[ind] = this.value !== '0' ? this.value : '';
                general.setData(stData, 24);
            };
        }; // 2}}}

        /**
         * @method listenSound {{{2
         */
        this.listenSound = function () {
            var _this = this;
            return function () {
                _this.playSound(this.previousElementSibling.value);
            };
        }; // 2}}}

        /**
         * @method showHideLink {{{2
         */
        this.showHideLink = function () {
            var stData = general.getData(24),
                persID = stData[0],
                tdLink = general.$('spPersLink'),
                butReset = general.$('spReset'),
                butSave = general.$('spSave');

            if (persID) {
                tdLink.innerHTML = '<a target="_blank" ' +
                    'style="color: #008000;" href="https://' + general.domain +
                    '/info.php?id=' + persID + '">' + stData[7] + '</a>';
                tdLink.style.display = '';
                butReset.disabled = false;
                butSave.disabled = true;
            } else {
                tdLink.style.display = 'none';
                butReset.disabled = true;
                butSave.disabled = false;
            }
        }; // 2}}}

        /**
         * @method scan {{{2
         */
        this.scan = function () {
            var stData = general.getData(24),
                url = 'https://' + general.domain + '/syndicate.php?id=' +
                    stData[1] + '&page=online',
                _this = this;

            this.ajax(url, 'GET', null, true, function (xml) {
                _this.spanContent.innerHTML = xml.responseText;
                var pers = _this.getPers(stData[0]);

                // в игре
                if (pers && !stData[6]) {
                    _this.playSound(stData[4]);
                    stData[6] = '1';
                    general.setData(stData, 24);

                    if (stData[3]) {
                        alert('Персонаж ' + stData[7] + ' в игре');
                    }
                }

                // вышел
                if (!pers && stData[6]) {
                    _this.playSound(stData[5]);
                    stData[6] = '';
                    general.setData(stData, 24);

                    if (stData[3]) {
                        alert('Персонаж ' + stData[7] + ' вышел из игры');
                    }
                }
            }, function () {
                general.root.setTimeout(function () {
                    _this.scan();
                }, 1000);
            });
        }; // 2}}}

        /**
         * @method start {{{2
         */
        this.start = function () {
            var _this = this;
            general.root.setInterval(function () {
                var stData = general.getData(24),
                    lastQuery = +stData[8],
                    now = new Date().getTime();
                if (stData[0] && now - lastQuery > _this.interval * 1000) {
                    stData[8] = now;
                    general.setData(stData, 24);
                    _this.scan();
                }
            }, Math.round(this.interval * 1000 + 3000 * Math.random()));
        }; // 2}}}

        /**
         * @method getPers {{{2
         * @param   {String}    id
         * @return  {Element|null}
         */
        this.getPers = function (id) {
            var css1 = 'center+br+table[align="center"]',
                css2 = 'center+br+script+table[align="center"]',
                table = this.spanContent.querySelector(css1) ||
                            this.spanContent.querySelector(css2);

            css1 = 'a[href$="/info.php?id=' + id + '"]>b';
            return table ? table.querySelector(css1) : null;
        }; // 2}}}

        /**
         * @method saveData {{{2
         */
        this.saveData = function () {
            var reg = /^\d+$/,
                persID = general.$('spID').value,
                syndID = general.$('spSyndID').value,
                str;

            if (!reg.test(persID)) {
                str = 'персонажа';
            } else if (!reg.test(syndID)) {
                str = 'синдиката';
            }

            if (str) {
                alert('Не верно введен ID ' + str);
                return;
            }

            var preloader = general.$('spPreloader'),
                url = 'https://' + general.domain + '/syndicate.php?id=' +
                    syndID + '&page=members',
                _this = this;

            preloader.style.visibility = 'visible';
            this.ajax(url, 'GET', null, true, function (xml) {
                _this.spanContent.innerHTML = xml.responseText;
                var pers = _this.getPers(persID);

                if (!pers) {
                    alert('Персонаж с ID ' + persID + ' в синдикате #' +
                        syndID + ' не найден');
                    preloader.style.visibility = 'hidden';
                    return;
                }

                var stData = general.getData(24);
                stData[0] = persID;
                stData[1] = syndID;
                stData[7] = pers.innerHTML;
                stData[8] = '';
                general.setData(stData, 24);

                _this.showHideLink();
                preloader.style.visibility = 'hidden';

                _this.start();
            }, function () {
                general.root.setTimeout(function () {
                    _this.saveData();
                }, 1000);
            });
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            var topPanel = new GetTopPanel().init();
            if (!topPanel) {
                return;
            }

            var stData = general.getData(24);
            // совместимость с прошлыми версиями
            if (stData[0] && !/^\d+$/.test(stData[0])) {
                stData = ['', '', '', '', '', '', '', '', ''];
                general.setData(stData, 24);
            }

            // кнопка настроек
            var scanPersBut = general.doc.createElement('span');
            scanPersBut.setAttribute('style', 'cursor: pointer;');
            scanPersBut.innerHTML = 'ScanPers';
            topPanel.appendChild(general.doc.createTextNode(' | '));
            topPanel.appendChild(scanPersBut);
            scanPersBut.addEventListener('click', this.showSettings(), false);

            // окно настроек
            var scanPersSettings = general.doc.createElement('div');
            scanPersSettings.setAttribute('id', 'spSettings');
            scanPersSettings.setAttribute('style', 'visibility: hidden; ' +
                'position: absolute; padding: 3px; border: solid 1px #339933;' +
                ' background: #D7F4D8; border-radius: 4px; box-shadow: ' +
                '5px 6px 6px rgba(122,122,122,0.5); z-index: 999;');

            var getSelectSound = new GetSelectSound().init;
            scanPersSettings.innerHTML = '<table>' +
                '<tr>' +
                    '<td>ID персонажа:</td>' +
                    '<td><input id="spID" value="" size="10" maxlength="7" ' +
                    'style="border: solid 1px #339933;"></td></tr>' +
                '<tr>' +
                    '<td>ID синдиката:</td>' +
                    '<td><input id="spSyndID" size="10" maxlength="5" ' +
                        'value="" style="border: solid 1px #339933;">' +
                    '</td></tr>' +
                '<tr>' +
                    '<td colspan="2">' +
                        '<input id="spChkSound" type="checkbox">' +
                        '<label for="spChkSound"> Проигрывать звук при:' +
                        '</label></td></tr>' +
                '<tr>' +
                    '<td colspan="2">входе&nbsp;&nbsp;&nbsp;&nbsp;' +
                        getSelectSound('spSound1') + '<br>' +
                    'выходе ' + getSelectSound('spSound2') + '</td></tr>' +
                '<tr>' +
                    '<td colspan="2">' +
                        '<input id="spChkAllert" type="checkbox">' +
                        '<label for="spChkAllert"> Выдавать сообщение' +
                        '</label></td></tr>' +
                '<tr>' +
                    '<td colspan="2" style="text-align: center;">' +
                        '<input type="button" id="spSave" value="Сохранить">' +
                        '<img id="spPreloader" src="' + general.imgPath +
                        'preloader.gif" style="margin-left: 10px; ' +
                        'visibility: hidden;" alt="img" />' +
                        '<input type="button" id="spReset" value="Сброс" ' +
                        'style="margin-left: 20px;"></td></tr>' +
                '<tr>' +
                    '<td id="spPersLink" colspan="2" style="text-align: ' +
                        'center; display: none;"></td></tr>' +
                '</table>';
            general.doc.body.appendChild(scanPersSettings);

            // ID перса и синда
            var inpPersID = general.$('spID'),
                inpSyndID = general.$('spSyndID');

            if (stData[0]) {
                inpPersID.value = stData[0];
                inpSyndID.value = stData[1];
            }

            // звук
            var chkSound = general.$('spChkSound'),
                sel1 = general.$('spSound1'),
                sel2 = general.$('spSound2'),
                listen1 = general.$('lspSound1'),
                listen2 = general.$('lspSound2');

            if (stData[2]) {
                sel1.disabled = false;
                sel2.disabled = false;
                listen1.disabled = false;
                listen2.disabled = false;
            }

            chkSound.checked = !!stData[2];
            chkSound.addEventListener('click', function () {
                var data = general.getData(24),
                    _this = this;

                sel1.disabled = !_this.checked;
                sel2.disabled = !_this.checked;
                listen1.disabled = !_this.checked;
                listen2.disabled = !_this.checked;
                data[2] = _this.checked ? '1' : '';
                general.setData(data, 24);
            }, false);

            sel1.value = stData[4] || '0';
            sel2.value = stData[5] || '0';
            sel1.addEventListener('change', this.changeSelect(4), false);
            sel2.addEventListener('change', this.changeSelect(5), false);

            // кнопки проигрывания звука
            listen1.addEventListener('click', this.listenSound(listen1), false);
            listen2.addEventListener('click', this.listenSound(listen2), false);

            // чекбокс "Выдавать сообщение"
            var chkAllert = general.$('spChkAllert');
            chkAllert.checked = !!stData[3];
            chkAllert.addEventListener('click', function () {
                var data = general.getData(24),
                    _this = this;
                data[3] = _this.checked ? '1' : '';
                general.setData(data, 24);
            });

            // кнопка сброса
            var _this = this;
            general.$('spReset').addEventListener('click', function () {
                if (confirm('Сбросить данные?')) {
                    var data = general.getData(24);
                    data[0] = '';
                    data[1] = '';
                    data[6] = '';
                    data[7] = '';
                    data[8] = '';
                    general.setData(data, 24);
                    inpPersID.value = '';
                    inpSyndID.value = '';
                    _this.showHideLink();
                }
            }, false);

            // кнопка сохранения данных
            var spSave = general.$('spSave');
            spSave.addEventListener('click', function () {
                _this.saveData();
            }, false);

            // нажатие <Enter> в полях ввода
            inpPersID.addEventListener('keypress', function (e) {
                var ev = e || general.root.event;
                if (ev.keyCode === 13 || ev.keyCode === 10) {
                    spSave.click();
                }
            }, false);

            inpSyndID.addEventListener('keypress', function (e) {
                var ev = e || general.root.event;
                if (ev.keyCode === 13 || ev.keyCode === 10) {
                    spSave.click();
                }
            }, false);

            this.showHideLink();
            this.start();
        }; // 2}}}
    }; // 1}}}

    /**
     * @class ShowInitMessOnForum {{{1
     * @constructor
     */
    var ShowInitMessOnForum = function () {
        /**
         * @property pageNum
         * @type {int}
         */
        this.pageNum = 0;
        /**
         * @property messages
         * @type {Array|null}
         */
        this.messages = null;

        /**
         * @method getMessagesOnPages {{{2
         * @param   {Object}    obj
         * @return  {NodeList}
         */
        this.getMessagesOnPages = function (obj) {
            return obj.querySelectorAll('td>table[cellpadding="5"]' +
                    '[cellspacing="0"][border="0"]');
        }; // 2}}}

        /**
         * @method insertMess {{{2
         * @param   {Element}  target
         * @param   {Element}  last
         */
        this.insertMess = function (target, last) {
            target = target.querySelector('tr');
            target.firstElementChild.
                setAttribute('style', 'padding-left: 20px;');

            // noinspection JSUnresolvedVariable
            var author = last.parentNode.previousElementSibling.
                    querySelector('b').innerHTML,
                lastLink = last.previousElementSibling.querySelector('a').href,
                tr = last.querySelector('tr:last-child').cloneNode(true),
                divMess = tr.querySelector('td>div[style$="overflow:hidden;"]'),
                messComplete = divMess.innerHTML,
                // если длина сообщения более 400 символов или в сообщении
                // более 3 переносов строк ('<br>'), то выводим половину
                // сообщения если его длина менее 400 или только первые 200
                // символов если его длина более 400 символов
                longMess = messComplete.length > 400 ||
                    divMess.querySelectorAll('br').length > 3,
                messHeader = author + '&nbsp;&nbsp;&nbsp;<a href="' + lastLink +
                    '">[&#8593;]</a><br>';

            // noinspection JSUnresolvedFunction
            divMess.parentNode.setAttribute('style', 'border: 1px dashed ' +
                '#339933; background: #C2EDC1;');
            // noinspection HtmlUnknownAttribute
            divMess.innerHTML = messHeader +
                (!longMess ? messComplete :
                        (messComplete.length > 400 ?
                                messComplete.substring(0, 200) : messComplete.
                            substring(0, Math.round(messComplete.length / 2))).
                            // убираем тэги <br> в конце сокращенного сообщения
                            replace(/\s*(<b?r?\s*\/?>?\s*)*$/, '').
                            // убираем незавершенную ссылку
                            replace(/<a href=[^>]+(>(https?|ftp):\/\/.*)?$/,
                                '') +
                        ' ...[<span style="text-decoration: underline; ' +
                        'color: #007700; cursor: pointer;" name="openMess">' +
                        'развернуть</span>]');
            target.parentNode.insertBefore(tr, target);

            if (longMess) {
                var openLink = divMess.querySelector('span[name="openMess"]');
                if (openLink) {
                    openLink.addEventListener('click', function () {
                        divMess.innerHTML = messHeader + messComplete;
                    }, false);
                }
            }
        }; // 2}}}

        /**
         * @method parseMessages {{{2
         * @param   {int}   ind
         */
        this.parseMessages = function (ind) {
            if (!this.messages[ind]) {
                return;
            }

            var messDiv = this.messages[ind].querySelector('div');
            if (!messDiv) {
                ind++;
                this.parseMessages(ind);
                return;
            }

            var reg = /^\s*\+?\s*(\d+)(,\D+|\.\D+|\)|\s|:|\+\D+)/,
                numReply = reg.exec(messDiv.innerHTML);

            numReply = numReply ? +numReply[1] : 0;

            // нет номера/номер === 0 или число > текущего сообщения
            if (numReply < 1 || numReply >= this.pageNum * 20 + ind + 1) {
                ind++;
                this.parseMessages(ind);
                return;
            }

            // порядковый номер сообщения на странице, на которое отвечаем
            var sequenceNum = numReply < 21 ? numReply - 1 :
                    numReply % 20 !== 0 ? numReply % 20 - 1 : 19,
                // страница, где находится сообщение, на которое отвечаем
                // (на каждой странице 20 сообщений)
                pageReply = Math.floor((numReply - 1) / 20);

            // если сообщение, на которое отвечаем,
            // находится на текущей странице
            if (pageReply === this.pageNum) {
                this.insertMess(this.messages[ind], this.messages[sequenceNum]);
                ind++;
                this.parseMessages(ind);
            } else {
                var url = general.loc.replace(/&page_id=\d+(#\d+)?#?/g, '') +
                        '&page_id=' + pageReply,
                    _this = this;

                new AjaxQuery().init(url, 'GET', null, true, function (xml) {
                    var spanContent = general.doc.createElement('span');
                    spanContent.innerHTML = xml.responseText;

                    var mess = _this.getMessagesOnPages(spanContent);
                    _this.insertMess(_this.messages[ind], mess[sequenceNum]);
                    ind++;
                    general.root.setTimeout(function () {
                        _this.parseMessages(ind);
                    }, 1000);
                }, function () {
                    general.root.setTimeout(function () {
                        _this.parseMessages(ind);
                    }, 1000);
                });
            }
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            if (/&page_id=\d+/.exec(general.loc)) {
                this.pageNum = +/&page_id=(\d+)/.exec(general.loc)[1];
            } else if (/page_id=last/.test(general.loc)) {
                var num = general.doc.querySelector('td[style="cursor:' +
                        'pointer;"][class="greenlightbg"]');
                if (num) {
                    this.pageNum = +num.firstElementChild.innerHTML;
                }
            }

            this.messages = this.getMessagesOnPages(general.doc);
            this.parseMessages(0);
        }; // 2}}}
    }; // 1}}}

    /**
     * @class SearchUser {{{1
     * @constructor
     */
    var SearchUser = function () {
        /**
         * @method init {{{2
         */
        this.init = function () {
            var topPanel = new GetTopPanel().init();
            if (topPanel) {
                var td = general.doc.createElement('td');
                td.setAttribute('style', 'width: 130px;');
                td.innerHTML = '<form name="fsearch" id="fsearch" ' +
                    'method="GET" action="/search.php">' +
                    '<input id="skey" name="key" value="" ' +
                    'style="width: 130px;" ' +
                    'title="Введите ник и нажмите Enter" /></form>';
                topPanel = general.DESIGN_VERSION === 'v2' ?
                        topPanel.parentNode : topPanel.parentNode.parentNode;
                topPanel.appendChild(td);

                general.$('skey').addEventListener('keypress', function (e) {
                    var ev = e || general.root.event;
                    if (ev.keyCode === 13) {
                        general.$('fsearch').submit();
                    }
                }, false);
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class SkillCounters {{{1
     * @constructor
     */
    var SkillCounters = function () {
        /**
         * @property counters
         * @type {NodeList}
         */
        this.counters = general.doc.querySelectorAll('td[valign="top"]' +
                '[bgcolor="#e9ffe9"]>div>table[border="0"] tr>td+td>nobr');
        /**
         * @property ids {{{2
         * @type {Array}
         */
        this.ids = [
            'cFighting', 'cEconomic', 'cProduction', 'cGuns', 'cGrenades',
            'cAuto', 'cHeavy', 'cShotguns', 'cSnipers'
        ]; // 2}}}
        /**
         * @property dataNow
         * @type {Array|null}
         */
        this.dataNow = null;

        /**
         * @method getHtime {{{2
         * @param   {int}   time
         * @return  {String}
         */
        this.getHtime = function (time) {
            var date = new Date(time),
                day = date.getDate(),
                str = '';

            str  += day < 10 ? '0' + day : day;
            str += '.';
            var month = date.getMonth() + 1;
            str += month < 10 ? '0' + month : month;
            str += '.';
            var year = /20(\d+)/.exec(date.getFullYear().toString())[1];
            str += year + ' ' + /(\d+:\d+):\d+/.exec(date.toString())[1];

            return str;
        }; // 2}}}

        /**
         * @method getValue {{{2
         * @param   {Element}   obj
         * @param   {int}       fix
         * @return  {String}
         */
        this.getValue = function (obj, fix) {
            return parseFloat(/\((\s?<span [^>]+>)?(\d+.?\d*)(\s?<\/span>)?\)/.
                exec(obj.innerHTML)[2]).toFixed(fix);
        }; // 2}}}

        /**
         * @method getDataNow {{{2
         * @param   {Boolean}   loadPage
         */
        this.getDataNow = function (loadPage) {
            var i;
            this.dataNow = [];
            for (i = 0; i < this.counters.length; i++) {
                this.dataNow.
                    push(this.getValue(this.counters[i], i < 3 ? 0 : 2));

                if (loadPage) {
                    this.counters[i].parentNode.parentNode.lastElementChild.
                        innerHTML = '<span id="' + this.ids[i] +
                        '" style="color: #FF0000; font-size: 9px;"></span>';
                }
            }

            var syndExp = general.doc.querySelector('span>b+nobr+nobr');
            if (syndExp && loadPage) {
                syndExp.innerHTML += '<span id="cSyndExp" ' +
                    'style="color: #FF0000; font-size: 9px;"></span> ' +
                    '<span id="syndLeftToLevel" style="font-size: 8px; ' +
                    'color: #696156;"></span>';
            }

            this.dataNow.push(syndExp ? this.getValue(syndExp, 0) : '');
            this.dataNow.push(new Date().getTime());
        }; // 2}}}

        /**
         * @method setLeftToLevel {{{2
         * @param   {int}   val
         */
        this.setLeftToLevel = function (val) {
            var syndLevels = [5, 15, 37, 76, 143, 200, 280, 500, 750, 1000,
                1250, 1600, 2200, 3000, 4500, 6000, 9000, 15000, 26394,
                34353, 44377, 56931, 72568, 91947, 115853, 145214, 181127,
                224882, 277996, 342247, 419713, 512821, 624395, 757716, 916591,
                1105426, 1329313, 1594124, 1906627, 2274598, 2723523, 3293658,
                4046236, 5077268, 6541333, 8693509, 11964817, 17100771,
                25421016, 40000000];

            var i;
            for (i = 0; i < syndLevels.length; i++) {
                if (val < syndLevels[i]) {
                    general.$('syndLeftToLevel').innerHTML = '[+' +
                        (syndLevels[i] - val) + ']';
                    break;
                }
            }
        }; // 2}}}

        /**
         * @method setCounters {{{2
         */
        this.setCounters = function () {
            var stData = general.getData(25),
                i;

            for (i = 0; i < this.counters.length; i++) {
                general.$(this.ids[i]).innerHTML = '[' +
                    (parseFloat(this.dataNow[i]) - parseFloat(stData[i])).
                        toFixed(i < 3 ? 0 : 2) + ']';
            }

            // синдовый уровень
            if (this.dataNow[9]) {
                // основной синдикат есть, а прошлого значения синдового нет
                if (!stData[9]) {
                    stData[9] = this.dataNow[9];
                    general.setData(stData, 25);
                }

                // noinspection JSRemoveUnnecessaryParentheses
                general.$('cSyndExp').innerHTML = '[' +
                    (+this.dataNow[9] - (+stData[9])) + ']';
                this.setLeftToLevel(+this.dataNow[9]);
            }
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            // на главной странице личного NPC
            if (/\?nid=\d+/.test(general.loc)) {
                return;
            }

            this.getDataNow(true);
            var stData = general.getData(25);

            if (!stData[0]) {
                stData = this.dataNow;
                general.setData(stData, 25);
            }

            // кнопа сброса счетчиков
            var tr = general.doc.createElement('tr');
            tr.innerHTML = '<td></td><td colspan="2" style="font-weight: ' +
                'bold; font-size: 9px;"><span id="resetCounters" ' +
                'style="cursor: pointer; color: #008000; text-decoration: ' +
                'underline;">Сбросить счетчики</span><br>' +
                '<span id="timeLastReset" style="cursor: default; color: ' +
                '#0000FF;">(' + this.getHtime(+stData[10]) + ')</span></td>';
            general.$('cSnipers').parentNode.parentNode.parentNode.
                appendChild(tr);

            var _this = this;
            general.$('resetCounters').addEventListener('click', function () {
                if (confirm('Сбросить счетчики?')) {
                    _this.getDataNow(false);
                    general.setData(_this.dataNow, 25);
                    general.$('timeLastReset').innerHTML = _this.
                        getHtime(_this.dataNow[10]);
                    _this.setCounters();
                }
            }, false);

            this.setCounters();
        }; // 2}}}
    }; // 1}}}

    /**
     * @class SyndPtsAnalyser {{{1
     * @constructor
     */
    var SyndPtsAnalyser = function () {
        /**
         * @property syndId
         * @type {String}
         */
        this.syndId = /\?id=(\d+)/.exec(general.loc)[1];
        /**
         * @property tm
         * @type {int}
         */
        this.tm = 1500;
        /**
         * @property mainTable
         * @type {Element}
         */
        this.mainTable = general.doc.querySelector('center+br+table') ||
            general.doc.querySelector('center+br+script+table');
        /**
         * @property lastDate
         * @type {String}
         */
        this.lastDate = '';
        /**
         * @property pers
         * @type {Array|null}
         */
        this.pers = null;
        /**
         * @property soExpForPTS
         * @type {Array|null}
         */
        this.soExpForPTS = null;
        /**
         * @property from
         * @type {int}
         */
        this.from = 0;
        /**
         * @property to
         * @type {int}
         */
        this.to = 0;
        /**
         * @property summ
         * @type {Array|null}
         */
        this.summ = null;
        /**
         * @property all
         * @type {int}
         */
        this.all = 0;
        /**
         * @property control
         * @type {int}
         */
        this.control = 0;
        /**
         * @property imgPath
         * @type {String}
         */
        this.imgPath = general.imgPath + 'SyndPtsAnalyser/';

        /**
         * @method getLastDate {{{2
         * @param   {String}    url
         */
        this.getLastDate = function (url) {
            var _url = url || 'https://' + general.domain +
                    '/syndicate.log.php?id=' + this.syndId +
                    '&ptslog=1&page_id=100500',
                counter = general.$('analizePTSCounter'),
                _this = this;

            if (!url) {
                counter.innerHTML = '2/1';
            }

            new AjaxQuery().init(_url, 'GET', null, true, function (xml) {
                var spanContent = general.doc.createElement('span');
                spanContent.innerHTML = xml.responseText;

                if (!url) {
                    counter.innerHTML = '2/2';
                    general.root.setTimeout(function () {
                        _this.getLastDate(spanContent.
                            querySelector('tr>td[class="greenbg"]' +
                                '[onclick*="window.location=\'' +
                                '/syndicate.log.php?"]:last-child>' +
                                'a[href*="/syndicate.log.php?id="]').href);
                    }, _this.tm);
                } else {
                    var fonts = spanContent.
                            querySelectorAll('nobr>font[color="green"]');
                    _this.lastDate = /\d+.\d+.\d+/.
                            exec(fonts[fonts.length - 1].innerHTML)[0];

                    var inpFrom = general.$('inpDateFrom');
                    inpFrom.value = _this.lastDate;
                    inpFrom.disabled = false;
                    general.$('inpDateTo').disabled = false;
                    general.$('goPTS').disabled = false;
                    general.$('ptsPreloader').style.display = 'none';
                }
            }, function () {
                var preloader = general.$('ptsPreloader');
                preloader.style.display = 'none';
                preloader.parentNode.innerHTML += '<br><span style="color: ' +
                    '#FF0000;">Ошибка ответа сервера...</span>';
            });
        }; // 2}}}

        /**
         * @method enterPress {{{2
         * @param   {Object}    e
         */
        this.enterPress = function (e) {
            var ev = e || general.root.event;
            if (ev.keyCode === 13) {
                general.$('goPTS').click();
            }
        }; // 2}}}

        /**
         * @method showRezult {{{2
         * @param   {String}    id
         */
        this.showRezult = function (id) {
            this.mainTable.removeAttribute('class');
            this.mainTable.setAttribute('style', 'border-collapse: ' +
                    'collapse; background: #D0EED0;');

            var str = '<tr style="font-weight: bold;"><td class="wb1">' +
                    'Персонаж</td><td class="wb1"><img id="gren" ' +
                    'style="cursor: pointer; margin: 2px;" src="' +
                    this.imgPath + 'gren.png" title="Покупка гранат" ' +
                    'alt="Гранаты"></td><td class="wb1"><img id="chip" ' +
                    'style="cursor: pointer; margin: 2px;" src="' +
                    this.imgPath + 'chip.png" title="Покупка чипов" ' +
                    'alt="Чипы"></td><td class="wb1"><img id="rank" ' +
                    'style="cursor: pointer; margin: 2px;" src="' +
                    this.imgPath + 'rank.png" title="Выдача званий" ' +
                    'alt="Звания"></td><td class="wb1"><img id="sign" ' +
                    'style="cursor: pointer; margin: 2px;" ' +
                    'src="https://images.' +
                    general.domain.replace('www.', '') + '/img/synds/' +
                    this.syndId + '.gif" title="Выдача знаков" alt="Знаки">' +
                    '</td><td class="wb1"><span id="all" ' +
                    'style="color: #008000; cursor: pointer;">Всего</span>' +
                    '</td></tr>',
                setPoints = new SetPoints().init;

            var getStrDate = new GetStrDate().init,
                txtAreaStr = 'Период анализа: ' +
                    getStrDate(this.from) + ' - ' + getStrDate(this.to) + '\n' +
                        '----------------------------------------------------' +
                            '---------\n\n',
                name,
                gren,
                chip,
                rank,
                sign,
                all,
                i;

            for (i = 0; i < this.pers.length; i++) {
                name = this.pers[i].name;
                gren = setPoints(this.pers[i].gren, '\'', false);
                chip = setPoints(this.pers[i].chip, '\'', false);
                rank = setPoints(this.pers[i].rank, '\'', false);
                sign = setPoints(this.pers[i].sign, '\'', false);
                all = setPoints(this.pers[i].all, '\'', false);

                str += '<tr><td class="wb2"><a target="_blank" ' +
                    'href="https://' + general.domain + '/search.php?key=' +
                    name + '" style="text-decoration: none; ' +
                    'font-weight: bold; color: #004400;">' + name +
                    '</a></td><td class="wb1">' + gren + '</td>' +
                    '<td class="wb1">' + chip + '</td><td class="wb1">' + rank +
                    '</td><td class="wb1">' + sign + '</td><td class="wb1" ' +
                    'style="color: #008000;">' + all + '</td></tr>';

                txtAreaStr += name + ':\nГранаты: ' + gren + ', Чипы: ' + chip +
                    ', Звания: ' + rank + ', Знак: ' + sign + ', Всего: ' +
                    all + '\n\n';
            }

            var s0 = setPoints(this.summ[0], '\'', false),
                s1 = setPoints(this.summ[1], '\'', false),
                s2 = setPoints(this.summ[2], '\'', false),
                s3 = setPoints(this.summ[3], '\'', false),
                control = setPoints(this.control, '\'', false);

            all = setPoints(this.all, '\'', false);
            str += '<tr style="font-weight: bold;"><td class="wb1" ' +
                'style="color: #0000FF;">Всего</td><td class="wb1" ' +
                'style="color: #0000FF;">' + s0 + '</td>' +
                '<td class="wb1" style="color: #0000FF;">' + s1 +
                '</td><td class="wb1" style="color: #0000FF;">' + s2 +
                '</td><td class="wb1" style="color: #0000FF;">' + s3 +
                '</td><td class="wb1" style="color: #FF0000;">' + all +
                '</td></tr><tr><td class="wb1" colspan="6"><b>Начислено за ' +
                'контроль</b>: <span style="color: #FF0000;">' + control +
                '</span> <b>PTS</b></td></tr>';

            var separator = '------------------------------------------------' +
                '--------------------------------------------------\n';
            txtAreaStr += separator + 'Всего:\n Гранаты: ' + s0 + ', Чипы: ' +
                s1 + ', Звания: ' + s2 + ', Знаки: ' + s3 + ', Всего: ' + all +
                '\n' + separator + 'Начислено за контроль: ' + control +
                ' PTS' + '\n' + separator;

            this.mainTable.innerHTML = str;

            if (!general.$('txtArea')) {
                var center = general.doc.createElement('center'),
                    tArea = general.doc.createElement('textarea');

                tArea.id = 'txtArea';
                tArea.setAttribute('cols', '90');
                tArea.setAttribute('rows', '10');
                tArea.setAttribute('readonly', 'true');
                center.appendChild(tArea);
                var table = this.mainTable,
                    prnt = table.parentNode,
                    br = general.doc.createElement('br');
                prnt.insertBefore(br, table);
                prnt.insertBefore(center, br);
            }

            var txtArea = general.$('txtArea');
            txtArea.value = txtAreaStr;

            // если есть переводы синдового опыта за PTS выводим их
            var len = this.soExpForPTS.length;
            if (len) {
                var div = general.doc.createElement('div');
                div.setAttribute('style', 'margin-top: 15px; width: 650px; ' +
                        'text-align: left;');
                txtArea.parentNode.appendChild(div);
                for (i = 0; i < len; i++) {
                    div.appendChild(this.soExpForPTS[i]);
                }
            }

            var titleSort = general.$(id);
            titleSort.parentNode.style.background = '#A0EEA0';

            general.$('gren').
                addEventListener('click', this.titleClick('gren'), false);
            general.$('chip').
                addEventListener('click', this.titleClick('chip'), false);
            general.$('rank').
                addEventListener('click', this.titleClick('rank'), false);
            general.$('sign').
                addEventListener('click', this.titleClick('sign'), false);
            general.$('all').
                addEventListener('click', this.titleClick('all'), false);
        }; // 2}}}

        /**
         * @method titleClick {{{2
         * @param   {String}    id
         */
        this.titleClick = function (id) {
            var _this = this;
            return function () {
                _this.sortPers(id);
            };
        }; // 2}}}

        /**
         * @method sortPers {{{2
         * @param   {String}    prop
         */
        this.sortPers = function (prop) {
            this.pers.sort(function (a, b) {
                var ret;

                if (a[prop] < b[prop]) {
                    ret = 1;
                } else if (a[prop] > b[prop]) {
                    ret = -1;
                } else {
                    ret = 0;
                }

                return ret;
            });

            this.showRezult(prop);
        }; // 2}}}

        /**
         * @method addData {{{2
         * @param   {Object}    pers
         * @param   {String}    prop
         * @param   {int}       val
         */
        this.addData = function (pers, prop, val) {
            pers[prop] += val;
            pers.all += val;

            var ind;
            switch (prop) {
            case 'gren':
                ind = 0;
                break;
            case 'chip':
                ind = 1;
                break;
            case 'rank':
                ind = 2;
                break;
            case 'sign':
                ind = 3;
                break;
            default:
                break;
            }

            this.summ[ind] += val;
            this.all += val;
        }; // 2}}}

        /**
         * @method parsePTSProtocols {{{2
         * @param   {int}   ind
         */
        this.parsePTSProtocols = function (ind) {
            general.$('analizePTSCounter').innerHTML = ind;
            var url = 'https://' + general.domain + '/syndicate.log.php?id=' +
                    this.syndId + '&ptslog=1&page_id=' + ind,
                _this = this;

            new AjaxQuery().init(url, 'GET', null, true, function (xml) {
                var spanContent = general.doc.createElement('span');
                spanContent.innerHTML = xml.responseText;

                var lines = spanContent.
                        querySelectorAll('nobr>font[color="green"]');

                if (!lines.length) {
                    _this.sortPers('all');
                    return;
                }

                var getTimestamp = new GetTimestamp().init,
                    nobr1,
                    nobr2,
                    pers,
                    time,
                    rez,
                    str,
                    i;

                for (i = 0; i < lines.length; i++) {
                    time = getTimestamp(lines[i].innerHTML);
                    if (time > _this.to) {
                        continue;
                    }

                    if (time < _this.from) {
                        _this.sortPers('all');
                        return;
                    }

                    nobr1 = lines[i].parentNode;
                    nobr2 = nobr1.nextElementSibling;
                    str = nobr2.innerHTML;

                    rez = /(.*) получил звание .* \((\d+) PTS/.exec(str);
                    if (rez) {
                        pers = _this.getPers(rez[1]);
                        _this.addData(pers, 'rank', +rez[2]);
                        continue;
                    }

                    rez = /(.*) купил.* за (\d+) PTS/.exec(str);
                    if (rez) {
                        pers = _this.getPers(rez[1]);
                        _this.addData(pers, /чип/i.test(str) ? 'chip' : 'gren',
                            +rez[2]);
                        continue;
                    }

                    rez = /выдал значок персонажу (.*) \((\d+) PTS/.exec(str);
                    if (rez) {
                        pers = _this.getPers(rez[1]);
                        _this.addData(pers, 'sign', +rez[2]);
                        continue;
                    }

                    rez = /Продлено звание для (.*) за (\d+) PTS/.exec(str);
                    if (rez) {
                        pers = _this.getPers(rez[1]);
                        _this.addData(pers, 'rank', +rez[2]);
                        continue;
                    }

                    rez = /выдал звание .* персонажу (.*) \((\d+) PT/.exec(str);
                    // бывает так:
                    // LPRulez выдал звание Private персонажу shiftman (0 PTS)
                    if (rez && rez[2] !== '0') {
                        pers = _this.getPers(rez[1]);
                        _this.addData(pers, 'rank', +rez[2]);
                        continue;
                    }

                    rez = /Начислено \$.* и (\d+) PTS за контроль/.exec(str);
                    if (rez) {
                        _this.control += +rez[1];
                    }

                    // перевод синдового опыта за PTS
                    rez = /\d+ ед. синдикатного опыта за \d+ PTS/.exec(str);
                    if (rez) {
                        _this.soExpForPTS.push(nobr1);
                        _this.soExpForPTS.push(nobr2);
                        _this.soExpForPTS.push(general.doc.createElement('br'));
                    }
                }

                general.root.setTimeout(function () {
                    ind++;
                    _this.parsePTSProtocols(ind);
                }, _this.tm);
            }, function () {
                general.root.setTimeout(function () {
                    _this.parsePTSProtocols(ind);
                }, _this.tm);
            });
        }; // 2}}}

        /**
         * @method getPers {{{2
         * @param   {String}    nik
         * @return  {Object}
         */
        this.getPers = function (nik) {
            var i;
            for (i = 0; i < this.pers.length; i++) {
                if (this.pers[i].name === nik) {
                    return this.pers[i];
                }
            }

            var pers = {name: nik, gren: 0, chip: 0, sign: 0, rank: 0, all: 0};
            this.pers.push(pers);

            return pers;
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            var target = general.doc.
                    querySelector('td[colspan="3"]>a[href*="&ptslog=1"]').
                        parentNode;

            //css-ботва
            var style = general.doc.createElement('style');
            style.innerHTML = '.wb1 {text-align:center; padding-left:3px; ' +
                'border: 1px #339933 solid;} .wb2 {padding-left:3px; ' +
                'padding-right:3px; border: 1px #339933 solid;}';
            general.doc.querySelector('head').appendChild(style);

            if (target.lastElementChild.nodeName === 'BR') {
                target.removeChild(target.lastElementChild);
            }

            var butShowPTSAnalizePanel = general.doc.createElement('a');
            butShowPTSAnalizePanel.innerHTML = 'Анализ PTS';
            butShowPTSAnalizePanel.setAttribute('style', 'cursor: pointer');
            target.appendChild(general.doc.createTextNode(' | '));
            target.appendChild(butShowPTSAnalizePanel);

            var _this = this;
            butShowPTSAnalizePanel.addEventListener('click', function () {
                if (general.$('inpDateFrom')) {
                    return;
                }

                var table = _this.mainTable,
                    tArea = general.$('txtArea');

                if (tArea) {
                    table.parentNode.
                        removeChild(table.previousElementSibling);
                    table.parentNode.
                        removeChild(table.previousElementSibling);
                }

                table.setAttribute('class', 'wb');
                table.setAttribute('width', '600');
                table.setAttribute('cellpadding', '0');
                table.setAttribute('cellspacing', '0');
                table.setAttribute('style', 'margin-bottom: 50px;');

                var getStrDate = new GetStrDate().init;
                table.innerHTML = '<tr><td>' +
                    'Введите даты в формате дд.мм.гг<br>' +
                    'с: <input id="inpDateFrom" maxlength="8" ' +
                    'value="" style="width: 70px;" disabled> до: ' +
                    '<input id="inpDateTo" maxlength="8" value="' +
                    getStrDate('now')  + '" style="width: 70px;" disabled> ' +
                    '<input type="button" id="goPTS" value=">>" disabled>' +
                    '<span id="ptsPreloader" style="margin-left: 10px;">' +
                    '<img src="' + general.imgPath + 'preloader.gif" ' +
                    'alt="img" /><span id="analizePTSCounter" ' +
                    'style="color: #0000FF; margin-left: 10px;">2/0</span>' +
                    '</span></td></tr>';

                _this.getLastDate('');

                general.$('inpDateFrom').
                    addEventListener('keypress', _this.enterPress, false);
                general.$('inpDateTo').
                    addEventListener('keypress', _this.enterPress, false);

                var getTimestamp = new GetTimestamp().init;
                general.$('goPTS').addEventListener('click', function () {
                    _this.from = getTimestamp(general.$('inpDateFrom').value);
                    _this.to = getTimestamp(general.$('inpDateTo').value);

                    var dateStrNow = getStrDate('now');
                    if (!_this.from || !_this.to ||
                            _this.from < getTimestamp(_this.lastDate) ||
                                _this.to > getTimestamp(dateStrNow) ||
                                    _this.from > _this.to) {
                        alert('Не верно введена дата !!!\n' +
                            'Первая запись в протоколе: ' + _this.lastDate +
                            '\nСегодня: ' + dateStrNow);
                        return;
                    }

                    general.$('ptsPreloader').style.display = '';
                    general.$('inpDateFrom').disabled = true;
                    general.$('inpDateTo').disabled = true;
                    general.$('goPTS').disabled = true;

                    _this.pers = [];
                    _this.soExpForPTS = [];
                    // гранаты, чипы, звания, знаки
                    _this.summ = [0, 0, 0, 0, 0];
                    _this.all = 0;
                    _this.control = 0;
                    _this.parsePTSProtocols(0);
                }, false);
            }, false);
        }; // 2}}}
    }; // 1}}}

    /**
     * @class SyndAnalyser {{{1
     * @constructor
     */
    var SyndAnalyser = function () {
        /**
         * @property syndId
         * @type {String}
         */
        this.syndId = /\?id=(\d+)/.exec(general.loc)[1];
        /**
         * @property mainTable
         * @type {Element}
         */
        this.mainTable = general.doc.querySelector('center+br+table') ||
            general.doc.querySelector('center+br+script+table');
        /**
         * @property tm
         * @type {int}
         */
        this.tm = 1500;
        /**
         * @property lastDate
         * @type {String}
         */
        this.lastDate = '';
        /**
         * @property from
         * @type {int}
         */
        this.from = 0;
        /**
         * @property to
         * @type {int}
         */
        this.to = 0;
        /**
         * @property mainData
         * @type {Object|null}
         */
        this.mainData = null;

        /**
         * @class MainData {{{2
         * @constructor
         */
        var MainData = function () {
            /**
             * @property pers
             * @type {Array}
             */
            this.pers = [];
            /**
             * @property allProtect
             * @type {int}
             */
            this.allProtect = 0;
            /**
             * @property allAttaks
             * @type {int}
             */
            this.allAttaks = 0;
            /**
             * @property allTake
             * @type {int}
             */
            this.allTake = 0;
            /**
             * @property allPut
             * @type {int}
             */
            this.allPut = 0;
            /**
             * @property takenSynd
             * @type {Array}
             */
            this.takenSynd = [];
            /**
             * @property dismissedSynd
             * @type {Array}
             */
            this.dismissedSynd = [];
            /**
             * @property realEstate
             * @type {Object}
             */
            this.realEstate = {
                es: {mMoney: 0, pMoney: 0, mPTS: 0, pPTS: 0},
                uran: {mMoney: 0, pMoney: 0, mPTS: 0, pPTS: 0},
                bars: {mMoney: 0, pMoney: 0, mPTS: 0, pPTS: 0},
                another: {mMoney: 0, pMoney: 0, mPTS: 0, pPTS: 0}
            };
        }; // 2}}}

        /**
         * @method getLastDate {{{2
         * @param   {String}    url
         */
        this.getLastDate = function (url) {
            var _url = url || 'https://' + general.domain +
                    '/syndicate.log.php?id=' + this.syndId + '&page_id=100500',
                counter = general.$('syndAnalyseCounter'),
                _this = this;

            if (!url) {
                counter.innerHTML = '2/1';
            }

            new AjaxQuery().init(_url, 'GET', null, true, function (xml) {
                var spanContent = general.doc.createElement('span');
                spanContent.innerHTML = xml.responseText;

                if (!url) {
                    counter.innerHTML = '2/2';
                    general.root.setTimeout(function () {
                        _this.getLastDate(spanContent.
                            querySelector('tr>td[class="greenbg"]' +
                                '[onclick*="window.location=\'' +
                                '/syndicate.log.php?"]:last-child>' +
                                'a[href*="/syndicate.log.php?id="]').href);
                    }, _this.tm);
                } else {
                    var fonts = spanContent.
                            querySelectorAll('nobr>font[color="green"]');
                    _this.lastDate = /\d+.\d+.\d+/.
                            exec(fonts[fonts.length - 1].innerHTML)[0];

                    var inpFrom = general.$('inpDateFrom');
                    inpFrom.value = _this.lastDate;
                    inpFrom.disabled = false;
                    general.$('inpDateTo').disabled = false;
                    general.$('goSAnalyse').disabled = false;
                    general.$('syndAnalysePreloader').style.display = 'none';
                }
            }, function () {
                var preloader = general.$('syndAnalysePreloader');
                preloader.style.display = 'none';
                preloader.parentNode.innerHTML += '<br>' +
                    '<span style="color: #FF0000;">Ошибка ответа сервера...' +
                    '</span>';
            });
        }; // 2}}}

        /**
         * @method enterPress {{{2
         * @param   {Object}    e
         */
        this.enterPress = function (e) {
            var ev = e || general.root.event;
            if (ev.keyCode === 13) {
                general.$('goSAnalyse').click();
            }
        }; // 2}}}

        /**
         * @method getPers {{{2
         * @param   {Object}   link
         * @return  {Object}
         */
        this.getPers = function (link) {
            var i;
            for (i = 0; i < this.mainData.pers.length; i++) {
                if (this.mainData.pers[i].link === link.href) {
                    return this.mainData.pers[i];
                }
            }

            var pers = {
                link: link.href,
                name: link.firstElementChild.innerHTML,
                attaks: 0,
                putMoney: 0,
                takeMoney: 0
            };

            this.mainData.pers.push(pers);

            return pers;
        }; // 2}}}

        /**
         * @method getTypeLine {{{2
         * @param   {String}    str
         * @return  {int|Object}
         */
        this.getTypeLine = function (str) {
            if (/инициировал нападение/i.test(str)) {
                return 1;
            }

            if (/На контролируемый объект/i.test(str)) {
                return 2;
            }

            if (/снято со счета/i.test(str)) {
                return {takeOff: +/\$(\d+)/.exec(str)[1]};
            }

            if (/переведено на счет/i.test(str)) {
                return {takeOn: +/\$(\d+)/.exec(str)[1]};
            }

            if (/на нападения на электростанции/i.test(str)) {
                return 3;
            }

            if (/на нападения на рудники/i.test(str)) {
                return 4;
            }

            if (/нападениях на остальную/i.test(str)) {
                return 5;
            }

            if (/принят/i.test(str)) {
                return 6;
            }

            if (/вышел/i.test(str)) {
                return 7;
            }

            if (/в нападениях за контроль баров/i.test(str)) {
                return 8;
            }

            // выгнали
            if (/покинул синдикат/i.test(str)) {
                return 9;
            }

            return 0;
        }; // 2}}}

        /**
         * @method getPersLink {{{2
         * @param    {Element}  elem
         * @return   {Element}
         */
        this.getPersLink = function (elem) {
            // noinspection JSUnresolvedVariable
            return elem.parentNode.nextElementSibling.querySelector('a');
        }; // 2}}}

        /**
         * @method parseLine {{{2
         * @param   {String}    str
         * @param   {Object}    obj
         */
        this.parseLine = function (str, obj) {
            var rez = /\$(\d+).*и (\d+).*\$(\d+).*и (\d+)/.exec(str);

            if (rez) {
                obj.mMoney += +rez[1].replace(/,/g, '');
                obj.mPTS += +rez[2].replace(/,/g, '');
                obj.pMoney += +rez[3].replace(/,/g, '');
                obj.pPTS += +rez[4].replace(/,/g, '');
            }
        }; // 2}}}

        /**
         * @method getRealEstateStr {{{2
         * @param   {Object}    obj
         * @param   {String}    name
         * @param   {String}    title
         * @param   {String}    str1
         * @param   {String}    str2
         * @param   {String}    str3
         * @return  {String}
         */
        this.getRealEstateStr = function (obj, name, title, str1, str2, str3) {
            var setPoints = new SetPoints().init;

            return title + name + str3 +
                str1 + 'Потрачено гб' + str2 + '$' +
                setPoints(obj.mMoney, '\'', false) + str3 + str1 +
                'Заработано гб' + str2 + '$' +
                setPoints(obj.pMoney, '\'', false) + str3 +
                str1 + '<span style="font-weight: bold;">Итого:</span>' + str2 +
                '<span style="font-weight: bold;">$' +
                setPoints(obj.pMoney - obj.mMoney, '\'', true) + '</span>' +
                str3 + str1 + 'Потрачено PTS' + str2 +
                setPoints(obj.mPTS, '\'', false) + str3 +
                str1 + 'Заработано PTS' + str2 +
                setPoints(obj.pPTS, '\'', false) + str3 + str1 +
                '<span style="font-weight: bold;">Итого:</span>' + str2 +
                '<span style="font-weight: bold;">' +
                setPoints(obj.pPTS - obj.mPTS, '\'', true) + '</span>' +
                str3;
        }; // 2}}}

        /**
         * @method showRezult {{{2
         */
        this.showRezult = function () {
            this.sortPers();

            var setPoints = new SetPoints().init,
                main = this.mainData,
                str1 = '<tr><td class="wb">',
                str2 = '</td><td class="wb">',
                str3 = '</td></tr>',
                strTakeMoney = '',
                strPutMoney = '',
                strAttaks = '',
                persLink,
                pers,
                val,
                i;

            for (i = 0; i < main.pers.length; i++) {
                pers = main.pers[i];
                persLink = '<a target="_blank" style="font-weight: bold; ' +
                    'text-decoration: none; color: #004400;" ' +
                    'href="' + pers.link + '">' + pers.name + '</a>';

                if (pers.attaks) {
                    val = setPoints(pers.attaks, '\'', false);
                    strAttaks += str1 + persLink + str2 + val + str3;
                }

                if (pers.takeMoney) {
                    val = '$' + setPoints(pers.takeMoney, '\'', false);
                    strTakeMoney += str1 + persLink + str2 + val + str3;
                }

                if (pers.putMoney) {
                    val = '$' + setPoints(pers.putMoney, '\'', false);
                    strPutMoney += str1 + persLink + str2 + val + str3;
                }
            }

            var str4 = '<tr><td colspan="2" class="wb"><a target="_blank" ' +
                    'style="text-decoration: none; font-weight: bold; color: ' +
                    '#004400;" href="https://' + general.domain +
                    '/search.php?key=',
                strDismissedSynd = '',
                strTakenSynd = '';

            for (i = 0; i < main.dismissedSynd.length; i++) {
                pers = main.dismissedSynd[i][0];
                strDismissedSynd += str4 + pers + '">' + pers + '</a>, ' +
                    main.dismissedSynd[i][1] + str3;
            }

            for (i = 0; i < main.takenSynd.length; i++) {
                pers = main.takenSynd[i][0];
                strTakenSynd += str4 + pers + '">' + pers + '</a>, ' +
                    main.takenSynd[i][1] + str3;
            }

            var title = '<tr><td colspan="2" style="background: #D0EED0; ' +
                'text-align: center; font-weight: bold;">',
                r = main.realEstate;

            this.mainTable.innerHTML = title + 'Нападающие [' +
                'нападений: <span style="color: #FF0000;">' + main.allAttaks +
                '</span>, защит: <span style="color: #0000FF;">' +
                main.allProtect + '</span>, всего боев: <span style="color: ' +
                '#990000;">' + (main.allAttaks + main.allProtect) + '</span>]' +
                str3 + strAttaks +

                this.getRealEstateStr(r.es, 'Электростанции',
                        title, str1, str2, str3) +
                this.getRealEstateStr(r.uran, 'Урановые рудники',
                        title, str1, str2, str3) +
                this.getRealEstateStr(r.bars, 'Бары',
                        title, str1, str2, str3) +
                this.getRealEstateStr(r.another, 'Другая недвижимость',
                        title, str1, str2, str3) +

                title + 'Счет синдиката [$<span style="color: #990000;">' +
                setPoints(main.allPut - main.allTake, '\'', true) +
                '</span>]' + str3 + str1 + '<span style="font-weight: bold; ' +
                'color: #FF0000;">Взяли со счета</span> (всего)' + str2 +
                '<span style="font-weight: bold;">$' +
                setPoints(main.allTake, '\'', false) + '</span>' + str3 +
                strTakeMoney + str1 + '<span style="font-weight: bold; ' +
                'color: #0000FF;">Положили на счет</span> (всего)' + str2 +
                '<span style="font-weight: bold;">$' +
                setPoints(main.allPut, '\'', false) + '</span>' + str3 +
                strPutMoney + title + 'Состав' + str3 + '<tr><td colspan="2" ' +
                'class="wb"><span style="font-weight: bold; color: #FF0000;">' +
                'Вышли из синдиката</span> (всего ' +
                main.dismissedSynd.length + ')' + str3 + strDismissedSynd +
                '<tr><td colspan="2" class="wb"><span style="font-weight: ' +
                'bold; color: #0000FF;">Приняты в синдикат</span> (всего ' +
                main.takenSynd.length + ')' + str3 + strTakenSynd;
        }; // 2}}}

        /**
         * @method sortPers {{{2
         */
        this.sortPers = function () {
            this.mainData.pers.sort(function (a, b) {
                var ret;

                if (a.attaks < b.attaks) {
                    ret = 1;
                } else if (a.attaks > b.attaks) {
                    ret = -1;
                } else {
                    ret = 0;
                }

                return ret;
            });
        }; // 2}}}

        /**
         * @method parseSyndProtocols {{{2
         * @param   {int}   ind
         */
        this.parseSyndProtocols = function (ind) {
            general.$('syndAnalyseCounter').innerHTML = ind;
            var url = 'https://' + general.domain + '/syndicate.log.php?id=' +
                    this.syndId + '&page_id=' + ind,
                _this = this;

            new AjaxQuery().init(url, 'GET', null, true, function (xml) {
                var spanContent = general.doc.createElement('span');
                spanContent.innerHTML = xml.responseText;

                var lines = spanContent.
                        querySelectorAll('nobr>font[color="green"]');

                if (!lines.length) {
                    _this.showRezult();
                    return;
                }

                var getTimestamp = new GetTimestamp().init,
                    typeLine,
                    take,
                    pers,
                    time,
                    tmp,
                    str,
                    i;

                for (i = 0; i < lines.length; i++) {
                    time = getTimestamp(lines[i].innerHTML);
                    if (time > _this.to) {
                        continue;
                    }

                    if (time < _this.from) {
                        _this.showRezult();
                        return;
                    }

                    // noinspection JSUnresolvedVariable
                    str = lines[i].parentNode.nextElementSibling.innerHTML;
                    typeLine = _this.getTypeLine(str);

                    if (!typeLine) {
                        continue;
                    }

                    //перс положил или взял со счета
                    take = typeLine.takeOff || typeLine.takeOn;
                    if (take) {
                        pers = _this.getPers(_this.getPersLink(lines[i]));
                        if (typeLine.takeOff) {
                            pers.takeMoney += take;
                            _this.mainData.allTake += take;
                        } else {
                            pers.putMoney += take;
                            _this.mainData.allPut += take;
                        }

                        continue;
                    }

                    str = str.replace(/,/g, '');
                    switch (typeLine) {
                    case 1:
                        _this.getPers(_this.getPersLink(lines[i])).attaks++;
                        _this.mainData.allAttaks++;
                        break;
                    case 2:
                        _this.mainData.allProtect++;
                        break;
                    case 3:
                        _this.parseLine(str, _this.mainData.realEstate.es);
                        break;
                    case 4:
                        _this.parseLine(str, _this.mainData.realEstate.uran);
                        break;
                    case 5:
                        _this.parseLine(str, _this.mainData.realEstate.another);
                        break;
                    case 6:
                        _this.mainData.takenSynd.
                            push([_this.getPersLink(lines[i]).
                                    firstElementChild.innerHTML,
                                lines[i].innerHTML]);
                        break;
                    case 7:
                        _this.mainData.dismissedSynd.
                            push([_this.getPersLink(lines[i]).
                                    firstElementChild.innerHTML,
                                lines[i].innerHTML]);
                        break;
                    case 8:
                        _this.parseLine(str, _this.mainData.realEstate.bars);
                        break;
                    case 9:
                        // выгнали
                        tmp = /^(.*) покинул синдикат( \(.*\))/.exec(str);
                        if (tmp) {
                            _this.mainData.dismissedSynd.push([tmp[1],
                                lines[i].innerHTML + tmp[2]]);
                        }

                        break;
                    default:
                        break;
                    }
                }

                general.root.setTimeout(function () {
                    ind++;
                    _this.parseSyndProtocols(ind);
                }, _this.tm);
            }, function () {
                general.root.setTimeout(function () {
                    _this.parseSyndProtocols(ind);
                }, _this.tm);
            });

        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            var target = general.doc.
                    querySelector('td[colspan="3"]>a[href*="&ptslog=1"]').
                        parentNode;

            if (target.lastElementChild.nodeName === 'BR') {
                target.removeChild(target.lastElementChild);
            }

            var butShowAnalysePanel = general.doc.createElement('a');
            butShowAnalysePanel.innerHTML = 'Анализ активности';
            butShowAnalysePanel.setAttribute('style', 'cursor: pointer');
            target.appendChild(general.doc.createTextNode(' | '));
            target.appendChild(butShowAnalysePanel);

            var _this = this;
            butShowAnalysePanel.addEventListener('click', function () {
                if (general.$('inpDateFrom')) {
                    return;
                }

                _this.mainTable.setAttribute('class', 'wb');
                _this.mainTable.setAttribute('cellpadding', '0');
                _this.mainTable.setAttribute('cellspacing', '0');
                _this.mainTable.removeAttribute('style');

                var getStrDate = new GetStrDate().init;
                _this.mainTable.innerHTML = '<tr><td>' +
                    'Введите даты в формате дд.мм.гг<br>' +
                    'с: <input id="inpDateFrom" maxlength="8" ' +
                    'value="" style="width: 70px;" disabled> до: ' +
                    '<input id="inpDateTo" maxlength="8" value="' +
                    getStrDate('now')  + '" style="width: 70px;" disabled> ' +
                    '<input type="button" id="goSAnalyse" value=">>" ' +
                    'disabled><span id="syndAnalysePreloader" ' +
                    'style="margin-left: 10px;"><img src="' + general.imgPath +
                    'preloader.gif" alt="img" />' +
                    '<span id="syndAnalyseCounter" style="color: #0000FF; ' +
                    'margin-left: 10px;">2/0</span></span></td></tr>';

                _this.getLastDate('');

                general.$('inpDateFrom').
                    addEventListener('keypress', _this.enterPress, false);
                general.$('inpDateTo').
                    addEventListener('keypress', _this.enterPress, false);

                var getTimestamp = new GetTimestamp().init;
                general.$('goSAnalyse').addEventListener('click', function () {
                    _this.from = getTimestamp(general.$('inpDateFrom').value);
                    _this.to = getTimestamp(general.$('inpDateTo').value);

                    var dateStrNow = getStrDate('now');
                    if (!_this.from || !_this.to ||
                            _this.from < getTimestamp(_this.lastDate) ||
                                _this.to > getTimestamp(dateStrNow) ||
                                    _this.from > _this.to) {
                        alert('Не верно введена дата !!!\n' +
                            'Первая запись в протоколе: ' + _this.lastDate +
                            '\nСегодня: ' + dateStrNow);
                        return;
                    }

                    general.$('syndAnalysePreloader').style.display = '';
                    general.$('inpDateFrom').disabled = true;
                    general.$('inpDateTo').disabled = true;
                    general.$('goSAnalyse').disabled = true;

                    _this.mainData = new MainData();
                    _this.parseSyndProtocols(0);
                }, false);
            }, false);
        }; // 2}}}
    }; // 1}}}

    /**
     * @class ShowMyAchievements {{{1
     * @constructor
     */
    var ShowMyAchievements = function () {
        /**
         * @property divResult
         * @type {HTMLElement|null}
         */
        this.divResult = null;

        /**
         * @method addCloseButton {{{2
         */
        this.addCloseButton = function () {
            this.divResult.innerHTML += '<img id="closemyachiev" ' +
                'src="' + general.imgPath + 'close.gif" style="cursor: ' +
                'pointer;" alt="img" />';

            var _this = this;
            general.$('closemyachiev').addEventListener('click', function () {
                _this.divResult.style.visibility = 'hidden';
            }, false);
        }; // 2}}}

        /**
         * @method getAchievNow {{{2
         * @param   {Object}    obj
         * @return  {Object|null}
         */
        this.getAchievNow = function (obj) {
            return obj.querySelectorAll('td.simplewhitebg>' +
                    'font[color="#336633"]');
        }; // 2}}}

        /**
         * @method showData {{{2
         * @param   {Object}    ths
         */
        this.showData = function (ths) {
            var pos = new GetPos().init(ths);
            this.divResult.style.left = pos.x;
            this.divResult.style.top = pos.y + 25;
            this.divResult.style.visibility = 'visible';
            this.divResult.innerHTML = '<img src="' + general.imgPath +
                'preloader.gif" alt="img" />';

            var stData = general.getData(26),
                url = 'https://' + general.domain + '/info.ach.php?id=' +
                    general.myID;

            if (!stData[0]) {
                this.divResult.innerHTML = 'Не выбрано ни одной ачивки на ' +
                    '<a target="_blank" href="' + url + '">этой</a> странице.';
                this.addCloseButton();
            } else {
                var _this = this;
                new AjaxQuery().init(url, 'GET', null, true, function (xml) {
                    var spanContent = general.doc.createElement('span');
                    spanContent.innerHTML = xml.responseText;

                    var achievNow = _this.getAchievNow(spanContent),
                        str = '<table>',
                        i;

                    for (i = 0; i < achievNow.length; i++) {
                        if (new RegExp('(^|,)' + i + '(,|$)').test(stData[0])) {
                            // noinspection JSUnresolvedVariable
                            str += '<tr>' + achievNow[i].parentNode.parentNode.
                                innerHTML + '</tr>';
                        }
                    }

                    _this.divResult.innerHTML = str + '</table>';
                    _this.addCloseButton();
                }, function () {
                    _this.divResult.innerHTML = '<span style="color: ' +
                        '#FF0000;">Ошибка ответа сервера...</span>';
                    _this.addCloseButton();
                });
            }
        }; // 2}}}

        /**
         * @method setChkHandler {{{2
         */
        this.setChkHandler = function () {
            var chks = general.doc.querySelectorAll('input[id^="achiev"]'),
                str = '',
                i;

            for (i = 0; i < chks.length; i++) {
                if (chks[i].checked) {
                    str += /\d+/.exec(chks[i].id)[0] + ',';
                }
            }

            general.setData([str.replace(/,$/, '')], 26);
        }; // 2}}}

        /**
         * @method setCheckboxes {{{2
         */
        this.setCheckboxes = function () {
            var achievNow = this.getAchievNow(general.doc),
                stData = general.getData(26),
                target,
                prnt,
                chk,
                i;

            for (i = 0; i < achievNow.length; i++) {
                chk = general.doc.createElement('input');
                chk.type = 'checkbox';
                chk.id = 'achiev' + i;
                chk.checked = new RegExp('(^|,)' + i + '(,|$)').test(stData[0]);

                prnt = achievNow[i].parentNode;
                target = prnt.firstChild.nodeType === 3 ?
                            prnt.firstChild : prnt.firstChild.nextSibling;

                prnt.insertBefore(chk, target);
                chk.addEventListener('click', this.setChkHandler, false);
            }
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            var topPanel = new GetTopPanel().init();

            if (topPanel) {
                this.divResult = general.doc.createElement('div');
                this.divResult.setAttribute('style', 'visibility: hidden; ' +
                        'position: absolute; padding: 3px; background-color: ' +
                        '#E7FFE7; border: solid 1px #339933; ' +
                        'max-width: 300px; border-radius:5px; top:0; left:0; ' +
                        'box-shadow: 5px 6px 6px rgba(122,122,122,0.5);');
                general.doc.body.appendChild(this.divResult);

                var span = general.doc.createElement('span');
                span.innerHTML = 'Достижения';
                span.id = 'spanAchievements';
                span.setAttribute('style', 'cursor: pointer;');

                var _this = this;
                span.addEventListener('click', function () {
                    _this.showData(this);
                }, false);

                topPanel.appendChild(general.doc.createTextNode(' | '));
                topPanel.appendChild(span);

                // на странице своих ачивок
                if (general.loc.
                        indexOf('/info.ach.php?id=' + general.myID) !== -1) {
                    this.setCheckboxes();
                }
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class SyndOnlineOnMainPage {{{1
     * @constructor
     */
    var SyndOnlineOnMainPage = function () {
        /**
         * @property syndUnion
         * @type {HTMLElement|null}
         */
        this.syndUnion = null;
        /**
         * @property syndMain
         * @type {HTMLElement|null}
         */
        this.syndMain = null;

        // noinspection FunctionWithInconsistentReturnsJS
        /**
         * @method setSms {{{2
         * @return  {Boolean}
         */
        this.setSms = function () {
            var nobrs = general.$('friendsbody').querySelectorAll('nobr');
            if (nobrs.length) {
                var pLink, i;
                for (i = 0; i < nobrs.length; i++) {
                    pLink = nobrs[i].querySelector('a[href*="/info.php?id="]');
                    if (pLink) {
                        nobrs[i].innerHTML += '<a target="_blank" ' +
                            'style="margin-left: 3px;" ' +
                            'href="/sms-chat.php?id=' +
                            /\d+$/.exec(pLink.href)[0] + '">' +
                            '<img src="https://images.' +
                            general.domain.replace('www.', '') + '/img/' +
                            'letter-pc.png" alt="img" /></a>';
                    }
                }

                return false;
            }
        }; // 2}}}

        /**
         * @method getOnline {{{2
         * @param   {Boolean}   type
         */
        this.getOnline = function (type) {
            var target = general.$('friendsbody');
            if (!target.querySelector('div')) {
                target.innerHTML = '<div></div>' + (this.syndUnion ?
                        '<hr style="color: #C3C3C3;" /><div></div>' : '');
            }

            target = type ? target.lastElementChild : target.firstElementChild;
            target.innerHTML = '<img src="' + general.imgPath +
                'preloader.gif" alt="img" />';

            var url = (type ? this.syndUnion.href : this.syndMain.href) +
                    '&page=online',
                _this = this;

            new AjaxQuery().init(url, 'GET', null, true, function (xml) {
                var spanContent = general.doc.createElement('span');
                spanContent.innerHTML = xml.responseText;

                target.innerHTML = '<a href="' + url + '">' +
                    '<img src="https://images.' +
                    general.domain.replace('www.', '') + '/img/synds/' +
                    /\?id=(\d+)/.exec(url)[1] + '.gif" alt="img" /></a> (' +
                    /<b>(\d+) бойцов онлайн<\/b>/.
                        exec(spanContent.innerHTML)[1] + ')<br>';

                var cssSelector = 'table[class="bordersupdown"][width="100%"]',
                    trs = spanContent.querySelector(cssSelector).
                        querySelectorAll('tr');

                if (trs.length > 1) {
                    var nobr, pers, syndImg, war, i;
                    for (i = 1; i < trs.length; i++) {
                        syndImg = trs[i].querySelector('a[href*=' +
                            '"/syndicate.php?id="]');
                        pers = trs[i].querySelector('a[href*="/info.php?id="]');
                        war = trs[i].
                            querySelector('a[href*="/warlog.php?bid="]');

                        nobr = general.doc.createElement('nobr');
                        if (syndImg) {
                            nobr.appendChild(syndImg);
                        }

                        if (war) {
                            pers.setAttribute('style', 'color: #FF0000;');
                        }

                        nobr.appendChild(pers);
                        nobr.innerHTML += ' <a target="_blank" ' +
                            'href="https://' + general.domain +
                            '/sms-create.php?mailto=' +
                            pers.firstElementChild.innerHTML +
                            '"><img src="https://images.' +
                            general.domain.replace('www.', '') +
                            '/img/letter-pc.png" alt="img" /></a>';

                        target.appendChild(nobr);
                        target.innerHTML += i < trs.length - 1 ? ',<wbr>' : '';
                    }
                }
            }, function () {
                general.root.setTimeout(function () {
                    _this.getOnline(type);
                }, 1000);
            });
        }; // 2}}}

        /**
         * @method createLink {{{2
         * @param   {String}    name
         * @param   {Boolean}   type
         * @return  {HTMLElement}
         */
        this.createLink = function (name, type) {
            var link = general.doc.createElement('a');
            link.setAttribute('style', 'text-decoration: underline; ' +
                    'cursor: pointer;');
            link.innerHTML = name;
            var _this = this;
            link.addEventListener('click', function () {
                _this.getOnline(type);
            }, false);

            return link;
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            // гости, друзья - ставим конвертики для отправки письма
            var friends = general.doc.querySelector('a[onclick*="setfriends"]'),
                guests = general.doc.querySelector('a[onclick*="setvisitor"]');

            friends.addEventListener('click', this.setSms, false);
            guests.addEventListener('click', this.setSms, false);
            friends.click();

            // основной синдикат
            this.syndMain = general.doc.
                    querySelector('span>b+nobr>a[href*="/syndicate.php?id="]');

            if (this.syndMain) {
                var b = general.doc.createElement('b');
                b.appendChild(general.doc.createTextNode(' / '));
                b.appendChild(this.createLink('Основа', false));
                guests.parentNode.parentNode.appendChild(b);

                var url = this.syndMain.href + '&page=politics',
                    _this = this;

                new AjaxQuery().init(url, 'GET', null, true, function (xml) {
                    var spanContent = general.doc.createElement('span');
                    spanContent.innerHTML = xml.responseText;

                    var cssSelector = 'tr>td[colspan="3"]' +
                        '[class="greengreenbg"]>' +
                        'a[href*="/syndicate.php?id="]:last-child';
                    _this.syndUnion = spanContent.querySelector(cssSelector);

                    if (_this.syndUnion) {
                        b.appendChild(general.doc.createTextNode(' '));
                        b.appendChild(_this.createLink('Союз', true));
                    }
                }, function () {
                    general.cons.log('Error request to: ' + url);
                });
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class TimeKarma {{{1
     * @constructor
     */
    var TimeKarma = function () {
        /**
         * @method formatTime {{{2
         * @param   {int}   sec
         */
        this.formatTime = function (sec) {
            var m = Math.floor(sec / 60),
                s = sec % 60;

            if (!m && !s) {
                general.$('spanKarmaTimer').innerHTML = '&nbsp';
                general.setData('', 27);
                return;
            }

            m = m < 10 ? '0' + m : m;
            s = s < 10 ? '0' + s : s;
            general.$('karmaTimer').innerHTML = m + ':' + s;

            var _this = this;
            general.root.setTimeout(function () {
                _this.formatTime(sec - 1);
            }, 1000);
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            // на странице личных NPC не работает
            if (general.$('npc_log_book')) {
                return;
            }

            // поставили карму, запоминаем время
            if (/vote/.test(general.loc) &&
                    /Спасибо, Ваше мнение учтено/.
                        test(general.doc.body.innerHTML)) {
                general.setData(new Date().getTime().toString(), 27);
                return;
            }

            var time = +general.getData(27)[0];
            if (time) {
                var difference = new Date().getTime() - time;
                if (general.doc.querySelector('a[href*="/info.vote.php?id="]' +
                        '[title^="Отправить Ваш голос"]') ||
                            difference > 1800000) {
                    general.setData('', 27);
                    return;
                }

                var span = general.doc.createElement('span');
                span.setAttribute('id', 'spanKarmaTimer');
                span.setAttribute('style', 'margin-left: 5px; color: #07A703;');
                span.innerHTML = '» Вы сможете выставить карму через ' +
                    '<span id="karmaTimer" style="color: #056802;"></span>';

                var css = 'td[colspan="3"][class="greenbrightbg"]>' +
                        'table[width="100%"]',
                    target = general.doc.querySelector(css);

                if (target) {
                    var prnt = target.parentNode;
                    prnt.removeChild(target.nextElementSibling);
                    prnt.insertBefore(span, target.nextElementSibling);

                    var tm = +((1800000 - difference) / 1000).toFixed(0);
                    this.formatTime(tm);
                }
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class ImgPokemonsOnBattle {{{1
     * @constructor
     */
    var ImgPokemonsOnBattle = function () {
        /**
         * @method deleteImagePoks {{{2
         */
        this.deleteImagePoks = function () {
            var divs = general.doc.querySelectorAll('div[name="imagepokemon"]'),
                i;

            for (i = 0; i < divs.length; i++) {
                divs[i].parentNode.removeChild(divs[i]);
            }
        }; // 2}}}

        /**
         * @method showImagePoks {{{2
         */
        this.showImagePoks = function () {
            var enemies = general.doc.
                    querySelectorAll('div[style*="font-size:8pt;"]>' +
                        'span[class="battletags"]+nobr>b'),
                getPos = new GetPos().init,
                name,
                size,
                txt,
                pos,
                div,
                i;

            for (i = 0; i < enemies.length; i++) {
                txt = enemies[i].innerHTML;
                name = null;
                if (txt === 'Боец ОМОН') {
                    name = 'omon';
                    size = [50, 60];
                } else if (/\s\[NPC\]$/.test(txt)) {
                    name = 'personalnpc';
                    size = [30, 30];
                } else if (/(^[^\s]+)\s\[/.test(txt)) {
                    name = /(^[^\s]+)\s\[/.exec(txt)[1];
                    size = [70, 80];
                }

                if (name) {
                    pos = getPos(enemies[i].parentNode);
                    div = general.doc.createElement('div');
                    general.doc.body.appendChild(div);
                    div.setAttribute('style', 'position: absolute;');
                    div.setAttribute('name', 'imagepokemon');
                    div.style.left = String(pos.x > 200 ?
                            pos.x - size[0] - 5 : pos.x + 140);
                    div.style.top = String(pos.y);
                    div.innerHTML = '<img src="' + general.imgPath +
                        'ImgPokemonsOnBattle/' + name + '.png" ' +
                        'style="width: ' + size[0] + 'px; height: ' + size[1] +
                        'px;" title="' + name + '" alt="' + name + '" />';
                }
            }
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            this.showImagePoks();

            // JS-версия боя
            if (/\/b0\/btl\.php/.test(general.loc)) {
                var _this = this;
                general.root.setInterval(function () {
                    _this.deleteImagePoks();
                    _this.showImagePoks();
                }, 1000);
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class SoundSyndBattle {{{1
     * @constructor
     */
    var SoundSyndBattle = function () {
        /**
         * @property redLink
         * @type {HTMLElement|null}
         */
        this.redLink = null;

        /**
         * @method setTimer {{{2
         * @param   {int}   sec
         */
        this.setTimer = function (sec) {
            var m = Math.floor(sec / 60),
                s = sec % 60;

            m = m < 10 ? '0' + m : m;
            s = s < 10 ? '0' + s : s;
            this.redLink.innerHTML = this.redLink.innerHTML.
                replace(/\d+:\d+/, m + ':' + s);

            var _this = this;
            s = sec - 1;
            if (s > -1) {
                general.root.setTimeout(function () {
                    _this.setTimer(s);
                }, 1000);
            }
        }; // 2}}}

        /**
         * @method syndAlert {{{2
         * @param   {String}    dataCheck
         * @param   {String}    data
         * @param   {int}       sound
         * @param   {int}       tm
         */
        this.syndAlert = function (dataCheck, data, sound, tm) {
            general.root.setTimeout(function () {
                var stData = general.getData(28);
                if (stData[3] === dataCheck) {
                    stData[3] = data;
                    general.setData(stData, 28);
                    new PlaySound().init(sound);
                }
            }, tm);
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            var stData = general.getData(28);

            if (!general.doc.querySelector('a[href*="/me.php"]' +
                        '[title^="Наличность"]')) {
                return;
            }

            this.redLink = general.doc.
                    querySelector('a[style*="color:#ff0000;"]' +
                        '[title^="Ваш синдикат в нападении"]');

            if (!this.redLink) {
                stData[3] = '';
                general.setData(stData, 28);
                return;
            }

            // для нового оформления игры
            if (/\[(\d+):(\d+)\]/.
                    test(this.redLink.nextElementSibling.innerHTML)) {
                this.redLink = this.redLink.nextElementSibling;
            }

            var time = /\[(\d+):(\d+)\]/.exec(this.redLink.innerHTML),
                timeLimit = +stData[0],
                sound1 = +stData[1],
                sound2 = +stData[2];

            if (time && timeLimit > 14) {
                // noinspection JSRemoveUnnecessaryParentheses
                var t = +time[1] * 60 + (+time[2]);
                this.redLink.href = '/wargroup.php?war=attacks';
                this.setTimer(t);

                var getRandom = new GetRandom().init;
                if (!stData[3]) {
                    if (t > timeLimit) {
                        this.syndAlert('', '1', sound1, getRandom(0, 3000));
                        this.syndAlert('1', '2', sound2,
                                (t - timeLimit) * 1000 + getRandom(0, 3000));
                    } else {
                        this.syndAlert('', '2', sound2, getRandom(0, 3000));
                    }
                } else if (t > timeLimit && stData[3] === '2') {
                    this.syndAlert('2', '1', sound1, getRandom(0, 3000));
                    this.syndAlert('1', '2', sound2,
                            (t - timeLimit) * 1000 + getRandom(0, 3000));
                } else if (t <= timeLimit && stData[3] === '1') {
                    this.syndAlert('1', '2', sound2, getRandom(0, 3000));
                }
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class AdvForum {{{1
     * @constructor
     */
    var AdvForum = function () {
        /**
         * @property lifeTime
         * @type {int}
         */
        this.lifeTime = 3 * 24 * 60 * 60 * 1000;
        /**
         * @property parseLoc
         * @type {Array|null}
         */
        this.parseLoc = /\?fid=(\d+)(&tid=(\d+))?/.exec(general.loc);

        /**
         * @method getForumTable {{{2
         * @return  {HTMLElement}
         */
        this.getForumTable = function () {
            return general.doc.querySelector('center+br+center+table');
        }; // 2}}}

        /**
         * @method processingThemesList {{{2
         */
        this.processingThemesList = function () {
            var table = this.getForumTable(),
                tableContent = table.innerHTML,
                fid = this.toHex(this.parseLoc[1]);

            // настройки
            this.setSettins(table, fid);

            var stData = general.getData(29),
                json = JSON.parse(stData[7]),
                json1 = JSON.parse(stData[8]),
                trs = table.querySelectorAll('tr'),
                lastTheme = true,
                themeDataStorage,
                themeLink,
                imgClosed,
                imgEyes,
                tid,
                i;

            for (i = 1; i < trs.length; i++) {
                trs[i].style.display = '';
                themeLink = trs[i].querySelector('td>a');

                // прикрепленки пропускаем или скрываем, не отслеживаем
                if (themeLink.firstElementChild) {
                    if (stData[4]) {
                        trs[i].style.display = 'none';
                    }

                    continue;
                }

                tid = this.toHex(+/&tid=(\d+)/.exec(themeLink.href)[1]);
                // верхняя тема на первой странице
                if (/\?fid=\d+(&page_id=0)?$/.test(general.loc) && lastTheme) {
                    lastTheme = false;
                    // id первой темы в хранилище не равен id первой
                    // темы на странице - играем звук, запоминаем тему
                    if (json1[fid] !== tid) {
                        // если не первый раз заходим в данную ветку
                        if (json1[fid]) {
                            new PlaySound().init(stData[1]);
                        }

                        json1[fid] = tid;
                        stData[8] = JSON.stringify(json1);
                        general.setData(stData, 29);
                    }
                }

                // закрытые темы
                if (/тема закрыта/.test(trs[i].
                        querySelector('td:last-child').innerHTML)) {
                    // скрытие закрытой темы
                    if (stData[3]) {
                        trs[i].style.display = 'none';
                        continue;
                    }

                    // отметка скрытой темы
                    if (stData[0]) {
                        imgClosed = general.doc.createElement('img');
                        imgClosed.setAttribute('style', 'height: 10px; ' +
                                'width: 10px; margin-right: 3px;');
                        imgClosed.title = 'Тема закрыта';
                        imgClosed.src = general.imgPath + 'AdvForum/closed.png';
                        themeLink.parentNode.insertBefore(imgClosed, themeLink);
                    }
                }

                // есть отслеживаемые темы в текущей ветке форума
                if (json[fid]) {
                    themeDataStorage = json[fid][tid];

                    // текущая тема отслеживается
                    if (themeDataStorage) {
                        imgEyes = general.doc.createElement('img');
                        imgEyes.setAttribute('style', 'margin-right: ' +
                            '3px; height: 12px; width: 12px; cursor: ' +
                            'pointer;');
                        imgEyes.setAttribute('title',
                                'Отменить отслеживание темы');
                        themeLink.parentNode.
                            insertBefore(imgEyes, themeLink);
                        imgEyes.src = general.imgPath + 'AdvForum/';
                        imgEyes.addEventListener('click',
                            this.cleanStorage(fid, tid, tableContent), false);

                        // появилось новое сообщение
                        if (themeDataStorage.c !== /\d+/.exec(trs[i].
                                querySelectorAll('td')[2].innerHTML)[0]) {
                            imgEyes.src += 'eyesPlus.png';
                        } else {
                            imgEyes.src += 'eyes.png';
                        }
                    }
                }
            }

            var tm = +stData[2];
            if (tm) {
                general.root.setTimeout(function () {
                    general.root.location.reload();
                }, tm * 1000);
            }
        }; // 2}}}

        /**
         * @method cleanStorage {{{2
         * @param   {String}    f
         * @param   {String}    t
         * @param   {String}    tableHTML
         * @return  {Function}
         */
        this.cleanStorage = function (f, t, tableHTML) {
            var _this = this;

            return function () {
                var stData = general.getData(29),
                    json = JSON.parse(stData[7]),
                    tmp = {},
                    fid,
                    tid;

                for (fid in json) {
                    if (json.hasOwnProperty(fid)) {
                        // удаляется ветка форума
                        if (f && !t && fid === f && general.root.
                                confirm('Удалить все данные ветки?')) {
                            continue;
                        }

                        for (tid in json[fid]) {
                            if (json[fid].hasOwnProperty(tid)) {
                                // удаляется тема форума
                                if (f && t && fid === f && t === tid &&
                                        general.root.
                                            confirm('Не отслеживать тему?')) {
                                    continue;
                                }

                                if (_this.toDec(json[fid][tid].d) +
                                        _this.lifeTime > new Date().getTime()) {

                                    if (!tmp[fid]) {
                                        tmp[fid] = {};
                                    }

                                    tmp[fid][tid] = json[fid][tid];
                                }
                            }
                        }
                    }
                }

                stData[7] = JSON.stringify(tmp);
                general.setData(stData, 29);

                if (tableHTML) {
                    _this.getForumTable().innerHTML = tableHTML;
                    _this.processingThemesList();
                }
            };
        }; // 2}}}

        /**
         * @method toDec {{{2
         * @param   {String} hex
         * @return  {int}
         */
        this.toDec = function (hex) {
            return parseInt(hex, 16);
        }; // 2}}}

        /**
         * @method toHex {{{2
         * @param   {int}       dec
         * @return  {String}
         */
        this.toHex = function (dec) {
            return Number(dec).toString(16);
        }; // 2}}}

        /**
         * @method showHideForum {{{2
         */
        this.showHideForum = function () {
            return function () {
                var stData = general.getData(29),
                    f = stData[6].split(','),
                    _this = this,
                    ind = /\d+/.exec(_this.id)[0];

                f[ind] = _this.checked ? '1' : '';
                stData[6] = f.join(',');
                general.setData(stData, 29);
            };
        }; // 2}}}

        /**
         * @method setSettins {{{2
         * @param   {HTMLElement}   table
         * @param   {String}        fid
         */
        this.setSettins = function (table, fid) {
            // кнопка настройки
            var tableContent = table.innerHTML,
                imgSettins = general.doc.createElement('img');

            imgSettins.src = 'https://images.' +
                general.domain.replace('www.', '') + '/i/home/properties.gif';
            imgSettins.setAttribute('style', 'cursor: pointer; ' +
                    'margin-left: 10px;');
            imgSettins.setAttribute('title', 'Настройки');

            // кнопка сброса всех данных ветки
            var imgReset = general.doc.createElement('img');
            imgReset.src = 'https://images.' +
                general.domain.replace('www.', '') + '/i/home/questlog.gif';
            imgReset.setAttribute('style', 'cursor: pointer; ' +
                    'margin-left: 10px;');
            imgReset.setAttribute('title', 'Сбросить все данные ветки');
            imgReset.addEventListener('click',
                        this.cleanStorage(fid, '', tableContent), false);

            var td = table.querySelector('td');
            td.appendChild(imgSettins);
            td.appendChild(imgReset);

            var _this = this;
            imgSettins.addEventListener('click', function () {
                var themes = [
                        ['Официальные объявления', '1'],
                        ['Вопросы и помощь в игре', '49'],
                        ['Общий Форум', '27'],
                        ['Идеи и предложения', '2'],
                        ['Форум для неигровых тем', '22'],
                        ['Клуб Нытиков', '55'],
                        ['Объявления синдикатов', '38'],
                        ['Вступлю в синдикат', '56'],
                        ['Конкурсы', '3'],
                        ['Благодарности и поздравления', '4'],
                        ['Offline встречи', '6'],
                        ['Креатив', '23'],
                        ['Официальные объявления налоговой инспекции', '24'],
                        ['Официальные объявления суда', '11'],
                        ['Зал суда', '12'],
                        ['Зал бракосочетаний', '29'],
                        ['Дворец Бракосочетания', '50'],
                        ['Техническая поддержка', '17'],
                        ['Баги и глюки (общее)', '18'],
                        ['Баги и глюки (финансовые вопросы)', '19'],
                        ['Проблемы с боями', '20'],
                        ['Проблемы с персонажами', '33'],
                        ['Respect Hill', '25']
                    ],
                    stData = general.getData(29),
                    f = stData[6].split(','),
                    str = '',
                    i;

                for (i = 0; i < themes.length; i++) {
                    str += '<tr><td><a target="_blank" ' +
                        'href="https://' + general.domain + '/threads.php?' +
                        'fid=' + themes[i][1] + '">' + themes[i][0] + '</a>' +
                        '</td><td><input id="showForum_' + i + '" ' +
                        'type="checkbox"' + (f[i] ? ' checked' : '') +
                        ' /></td></tr>';
                }

                table.innerHTML = '<tr><td><table><tr><td>Отмечать закрытые ' +
                    'темы</td><td><input id="markClosed" type="checkbox" />' +
                    '</td></tr><tr><td>Не показывать закрытые темы</td>' +
                    '<td><input id="showClosed" type="checkbox" /></td></tr>' +
                    '<tr><td>Не показывать прикрепленки</td><td>' +
                    '<input id="showAttached" type="checkbox" /></td></tr>' +
                    '<tr><td>Интервал перезагрузки страниц с темами форума ' +
                    '(> 4)</td><td><input id="reloadInterval" ' +
                    'maxlength="3" style="width: 40px;" /> сек (0 или пустое ' +
                    'поле - без перезагрузки)</td></tr><tr><td>Звук при ' +
                    'появлении новой темы</td><td>' +
                    new GetSelectSound().init('sound') +
                    '</td></tr><tr><td>Номера исключенных веток форума<br>' +
                    '(параметр fid=xxx в ссылке на форум)</td><td>' +
                    '<input id="exclusion" style="width: ' +
                    '400px;" /></td></tr><tr><td colspan="2" ' +
                    'style="padding-top: 10px;"><span id="return" ' +
                    'style="cursor: pointer; color: #990000; font-weight: ' +
                    'bold;"><< Назад</span></td></tr><tr><td colspan="2" ' +
                    'style="font-weight: bold; padding-top: 10px;">' +
                    'Показывать ветки форума на <a target="_blank" ' +
                    'href="https://' + general.domain + '/forum.php">' +
                    'этой странице</a>:</td></tr>' + str + '</table></td></tr>';

                // чекбокс "Отмечать закрытые"
                var markClosed = general.$('markClosed');
                markClosed.checked = stData[0];
                markClosed.addEventListener('click', function () {
                    var data = general.getData(29);
                    data[0] = markClosed.checked ? '1' : '';
                    general.setData(data, 29);
                }, false);

                // чекбокс "Не показывать закрытые темы"
                var showClosed = general.$('showClosed');
                showClosed.checked = stData[3];
                showClosed.addEventListener('click', function () {
                    var data = general.getData(29);
                    data[3] = showClosed.checked ? '1' : '';
                    general.setData(data, 29);
                }, false);

                // чекбокс "Не показывать прикрепленки"
                var showAttached = general.$('showAttached');
                showAttached.checked = stData[4];
                showAttached.addEventListener('click', function () {
                    var data = general.getData(29);
                    data[4] = showAttached.checked ? '1' : '';
                    general.setData(data, 29);
                }, false);

                // текстовое поле "Интервал перезагрузки страниц"
                var reloadInterval = general.$('reloadInterval');
                reloadInterval.value = stData[2] || '0';
                reloadInterval.addEventListener('input', function () {
                    var data = general.getData(29),
                        val = +reloadInterval.value;

                    data[2] = val && val > 4 ? val.toString() : '';
                    general.setData(data, 29);
                }, false);

                // выбор и прослушивание звука
                var selSound = general.$('sound'),
                    lSound = general.$('lsound');

                selSound.disabled = false;
                lSound.disabled = false;
                lSound.addEventListener('click', function () {
                    new PlaySound().init(selSound.value);
                }, false);

                selSound.value = stData[1] || '0';
                selSound.addEventListener('change', function () {
                    var data = general.getData(29),
                        val = selSound.value;

                    data[1] = val === '0' ? '' : val;
                    general.setData(data, 29);
                }, false);

                // поле с номерами форумов-исключений
                var exclusion = general.$('exclusion');
                exclusion.value = stData[5];
                exclusion.addEventListener('input', function () {
                    var data = general.getData(29);
                    data[5] = exclusion.value.replace(/\s+/g, '');
                    general.setData(data, 29);
                }, false);

                // кнопка "Назад"
                general.$('return').addEventListener('click', function () {
                    table.innerHTML = tableContent;
                    _this.processingThemesList();
                }, false);

                // обработчики чекбоксов скрытия/показа форумов
                var chks = table.querySelectorAll('input[id^="showForum_"]');
                for (i = 0; i < chks.length; i++) {
                    chks[i].
                        addEventListener('click', _this.showHideForum(), false);
                }
            }, false);
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            var stData = general.getData(29),
                i;

            // на странице списка форумов
            if (/\/forum\.php$/.test(general.loc)) {
                var allBranches = general.doc.querySelectorAll('tr>' +
                        'td[valign="top"][onclick*="/threads.php?fid="]'),
                    f = stData[6].split(','),
                    j = 0;

                for (i = 0; i < allBranches.length; i++) {
                    if (!/Форум синдиката #\d+/.
                            test(allBranches[i].innerHTML)) {

                        // noinspection JSUnresolvedVariable
                        allBranches[i].parentNode.style.display = f[j] ?
                                                            '' : 'none';
                        j++;
                    }
                }

                return;
            }

            var fid = this.parseLoc[1];
            // исключенные форумы
            if (new RegExp('(^|,)' + fid + '(,|$)').test(stData[5])) {
                return;
            }

            fid = this.toHex(fid);
            // на странице сообщений в теме
            if (/\/messages\.php\?/.test(general.loc)) {
                var json = JSON.parse(stData[7]),
                    tid = this.toHex(this.parseLoc[3]);

                // нет ветки форума в хранилище
                if (!json[fid]) {
                    json[fid] = {};
                }

                // нет темы в хранилище
                if (!json[fid][tid]) {
                    json[fid][tid] = {
                        d: this.toHex(new Date().getTime()),
                        l: '0', // id последнего сообщения на странице
                        c: ''   // номер последнего сообщения
                    };
                }

                // просматриваем сообщения на странице
                var allMess = general.doc.querySelectorAll('td[id^="cella_"]'),
                    lastSavedId,
                    currMessId;

                for (i = 0; i < allMess.length; i++) {
                    lastSavedId = this.toDec(json[fid][tid].l);
                    currMessId = +/\d+/.exec(allMess[i].id)[0];
                    // или сообщение новое или еще не заходили в тему
                    if (lastSavedId < currMessId) {
                        // если это сообщение не наше и уже заходили
                        // в тему, устанавливаем метку "New"
                        if (/\?id=(\d+)/.exec(allMess[i].querySelector('b>a').
                                href)[1] !== general.myID && lastSavedId) {

                            allMess[i].innerHTML = '<span style="color: ' +
                                '#339900; font-weight: bold; float: right;">' +
                                '[New]</span>' + allMess[i].innerHTML;
                        }

                        // если сообщение последнее на странице
                        // запоминаем его данные в хранилище
                        if (!allMess[i + 1]) {
                            json[fid][tid].l = this.toHex(currMessId);
                            json[fid][tid].c = allMess[i].nextElementSibling.
                                querySelector('a').innerHTML;
                            stData[7] = JSON.stringify(json);
                            general.setData(stData, 29);
                        }
                    }
                }

                return;
            }

            // на странице списка тем ветки форума
            if (/\/threads\.php\?/.test(general.loc)) {
                var spanClean = general.doc.createElement('span');
                general.doc.body.appendChild(spanClean);
                spanClean.addEventListener('click',
                            this.cleanStorage('', '', ''), false);
                // удаляем старые темы из хранилища
                spanClean.click();

                this.processingThemesList();
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class DelAndAddBlackSms {{{1
     * @constructor
     */
    var DelAndAddBlackSms = function () {
        /**
         * @method init {{{2
         */
        this.init = function () {
            var del = general.doc.querySelector('td>a[class="mainbutton"]' +
                    '[href*="&do_black=1&addblack="]');

            if (del) {
                del.setAttribute('style', 'background: #FDD8D8;');

                del.addEventListener('click', function (e) {
                    if (!general.
                            root.confirm('Удалить и забанить. Уверены ???')) {

                        var ev = e || general.root.event;
                        ev.preventDefault();
                    }
                }, false);
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class Regeneration {{{1
     * @constructor
     */
    var Regeneration = function () {
        // noinspection JSUnusedGlobalSymbols
        /**
         * @property currentHp
         * @type {int}
         */
        this.currentHp = 0;
        /**
         * @property maxHp
         * @type {int}
         */
        this.maxHp = 0;
        /**
         * @property speedHpRecovery
         * @type {int}
         */
        this.speedHpRecovery = 0;
        /**
         * @property spanHP
         * @type {HTMLElement|null}
         */
        this.spanHP = null;
        /**
         * @property progressBar
         * @type {HTMLDivElement|null}
         */
        this.progressBar = null;
        /**
         * @property pbWidth
         * @type {int}
         */
        this.pbWidth = 230;
        /**
         * @property pbHeight
         * @type {int}
         */
        this.pbHeight = 4;

        /**
         * @method formatTime {{{2
         * @param   {int}   sec
         * @return  {String}
         */
        this.formatTime = function (sec) {
            var str = '';
            if (sec >= 3600) {
                var h = Math.floor(sec / 3600);
                str += '0' + h + ':';
                sec -= h * 3600;
            }

            var m = Math.floor(sec / 60);
            str += m ? (m < 10 ? '0' + m : m) : '00';
            str += ':';
            sec -= m * 60;

            str += sec < 10 ? '0' + sec : sec;
            return str;
        }; // 2}}}

        /**
         * @method hpupdate {{{2
         * @param   {Boolean}   first
         */
        this.hpupdate = function (first) {
            if (!first) {
                this.currentHp += this.speedHpRecovery;
            }

            if (this.currentHp > this.maxHp) {
                this.currentHp = this.maxHp;
            }

            // текущее HP в процентах
            var hpPercent = Math.floor((this.currentHp * 100) / this.maxHp);
            this.spanHP.innerHTML = '[' + hpPercent + '%]';

            // прогресс бар
            if (hpPercent >= 100) {
                // noinspection JSUnresolvedVariable
                this.progressBar.parentNode.style.display = 'none';
            } else if (hpPercent < 0) {
                // если кильнули
                this.progressBar.style.width = '0';
            } else {
                this.progressBar.style.width = String(Math.ceil(this.pbWidth *
                    hpPercent / 100) + 1);
            }

            //паказываем время
            var sec;
            if (hpPercent < 100) {
                sec = Math.floor((this.maxHp - this.currentHp) /
                        this.speedHpRecovery);
                this.spanHP.innerHTML += ' <span ' +
                    'style="font-weight: bold; color: #008000;">[' +
                    this.formatTime(sec) + ']</span>';
            }

            if (hpPercent < 80) {
                sec = Math.floor((this.maxHp * 0.8 - this.currentHp) /
                        this.speedHpRecovery);
                this.spanHP.innerHTML += ' <span ' +
                    'style="font-weight: bold; color: #FF0000;"> [' +
                    this.formatTime(sec)  + ']</span>';
            }

            var stData = general.getData(31),
                playSound = new PlaySound().init;

            if (hpPercent < 100 && hpPercent  >= 80 && !stData[2]) {
                stData[2] = '1';
            }

            if (hpPercent === 100 && stData[2] === '3') {
                stData[2] = '2';
            }

            if (stData[2] === '1') {
                playSound(stData[0]);
                stData[2] = '3';
            } else if (stData[2] === '2') {
                playSound(stData[1]);
                stData[2] = '4';
            } else if ((stData[2] === '3' || stData[2] === '4') &&
                    hpPercent < 100) {
                stData[2] = hpPercent < 80 ? '' : '3';
            }

            general.setData(stData, 31);

            var _this = this;
            if (stData[2] !== '4') {
                general.root.setTimeout(function () {
                    _this.hpupdate(false);
                }, 1000);
            }
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            // на главной странице личного NPC
            if (/\?nid=\d+/.test(general.loc)) {
                return;
            }

            var target = general.doc.querySelector('div#hpdiv');
            if (target) {
                var divHealth = general.doc.createElement('div');
                divHealth.setAttribute('style', 'color: #0000FF;');
                divHealth.innerHTML = '&nbsp;» ' +
                    '<span style="font-weight: bold;">Выздоровление:</span> ' +
                    '<span id="regenHpPercent"></span>' +
                    '<div style="width: 230px; border: 1px #BBCCBB solid; ' +
                            'margin: 2px 0 3px 3px; box-shadow: 1px 1px 3px ' +
                            'rgba(122,122,122,0.5);">' +
                        '<div id="progressBar" style="width: ' + this.pbWidth +
                            'px; height: ' + this.pbHeight + '; ' +
                            'background-image: url(' + general.imgPath +
                            'Regeneration/line.png);">' +
                        '</div>' +
                    '</div>';

                target = target.nextElementSibling;
                target.parentNode.insertBefore(divHealth, target.nextSibling);

                this.spanHP = general.$('regenHpPercent');
                this.progressBar = general.$('progressBar');

                // noinspection JSUnusedGlobalSymbols
                /** @namespace general.root.hp_start_h */
                this.currentHp = +general.root.hp_start_h;
                /** @namespace general.root.hp_max_h */
                this.maxHp = +general.root.hp_max_h;
                /** @namespace general.root.hp_speed_h */
                this.speedHpRecovery = parseFloat(general.root.hp_speed_h);

                this.hpupdate(true);
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class ProfColor {{{1
     * @constructor
     */
    var ProfColor = function () {
        /**
         * @property activeProfs
         * @type {NodeList}
         */
        this.activeProfs = general.doc.
                querySelectorAll('tr>td>font[color="#006600"]');

        /**
         * @method init {{{2
         */
        this.init = function () {
            var i;
            for (i = 0; i < this.activeProfs.length; i++) {
                this.activeProfs[i].setAttribute('color', '#FF0000');
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class CurrentQuestOnInfo {{{1
     * @constructor
     */
    var CurrentQuestOnInfo = function () {
        /**
         * @property persID
         * @type {String}
         */
        this.persID = /\?id=(\d+)/.exec(general.loc)[1];
        /**
         * @property tm
         * @type {int}
         */
        this.tm = 1200;
        /**
         * @property target
         * @type {Element|null}
         */
        this.target = general.doc.querySelector('#actiondivin');

        /**
         * @method showQuest {{{2
         * @param   {String}    url
         */
        this.showQuest = function (url) {
            var _this = this;
            new AjaxQuery().init(url, 'GET', null, true, function (xhr) {
                var spanContent = general.doc.createElement('span');
                spanContent.innerHTML = xhr.responseText;

                // noinspection JSUnresolvedVariable
                var cssSelector = 'td[valign="top"][align="right"]>' +
                        'a[href*="/help/index.php?sid="]',
                    td = spanContent.querySelector(cssSelector).parentNode.
                        previousElementSibling,
                    questDescr = td.firstElementChild.nextSibling.nodeValue,
                    reg = /^\s*(.*):\s*(\d+) из (\d+)/.exec(questDescr),
                    acQuests = />(\d+)</.exec(td.innerHTML),
                    bLevel = general.doc.
                        querySelector('span[onclick^="return pip_load("]').
                            previousElementSibling.firstElementChild.innerHTML;

                if (!reg || !acQuests || !bLevel) {
                    return;
                }

                var isDone = +reg[2] >= +reg[3],
                    span = general.doc.createElement('span');

                span.setAttribute('style', 'margin-left: 7px; font-size: 8pt;');
                span.innerHTML = '<span id="questDesc">' + reg[1] + ' [' +
                    '<a href="https://' + general.domain + '/questlog.php?id=' +
                    _this.persID + '" style="color: ' +
                    (isDone ? '#008700' : '#AA5500') + '; ' +
                    'text-decoration: none; font-size: 8pt;" target="_blank">' +
                    reg[2] + '</a>/' + reg[3] + ']</span> ' +
                    '(<a target="_blank" style="color:#007700; ' +
                    'font-weight: bold; text-decoration: none;" ' +
                    'href="https://' + general.domain + '/help/index.php?' +
                    'sid=102&pid=45">' + acQuests[1] + '</a>)' +
                    '<img src="https://images.' +
                    general.domain.replace('www.', '') + '/i/home/wlog.gif" ' +
                    'id="showHideQuestList" border="0" width="12" ' +
                    'height="10" style="margin-left: 3px; cursor: pointer;" ' +
                    'alt="img" />' +
                    '<div id="questList" style="display: none;">' +
                    '<ul>' +
                        '<li>Поймать рыбу 1 раз<br>' +
                            '<span style="color: #4E4E4E;">(ловить можно не ' +
                            'чаще, чем раз в 40 минут, с 8 до 11 и с 17 до ' +
                            '20 часов по серверу)</span>' +
                            '<li>Зайти на замену и выжить в 2 боях' +
                        '<li>Выжить в 5 боях на Outland<br>' +
                            '<span style="color: #4E4E4E;">(засчитывается и ' +
                            'в прибрежной зоне)</span>' +
                        '<li>Нанести в синдикатных боях суммарный урон в ' +
                            (bLevel * 20).toString() + ' HP' +
                        '<li>Убить хотя бы одного врага в 3 синдикатных ' +
                            'боях<br><span style="color: #4E4E4E;">(бои за ' +
                            'бункер не учитываются)</span>' +
                        '<li>Сделать 3 критических попадания из снайперской ' +
                            'винтовки или автомата<br>' +
                            '<span style="color: #4E4E4E;">(Если после боя ' +
                            'ломается оружие и персонаж оказывается с ' +
                            'пустыми руками, то все попадания, сделанные в ' +
                            'этом бою, не засчитаются. Для двуручного оружия ' +
                            'криты считаются с левой, при этом в правой ' +
                            'должен быть тип оружия, на которое в квесте ' +
                            'запрошены криты.)</span>' +
                        '<li>Сделать 30 критических попаданий из пулемета' +
                        '<li>На Outland нанести Z-Lands суммарный урон ' +
                            (bLevel * 20).toString() + ' HP' +
                        '<li>Убить гранатой 2 Z-Lands<br>' +
                            '<span style="color: #4E4E4E;">(горение идёт в ' +
                            'зачёт)</span>' +
                        '<li>Привезти 5 предметов с Outland' +
                        '<li>Сделать 4 критических попаданий из ' +
                            'пистолетов-пулеметов<br>' +
                            '<span style="color: #4E4E4E;">(для двуручного ' +
                            'оружия криты считаются с левой, при этом в ' +
                            'правой должен быть тип оружия, на которое в ' +
                            'квесте запрошены криты)</span>' +
                            '<li>Выполнить 2 задания NPC' +
                        '<li>Убить 3 врагов в уличных боях' +
                        '<li>Выжить в 5 общих групповых боях<br>' +
                            '<span style="color: #4E4E4E;">(в зачёт идут ' +
                            'нападения на бункер для всех)</span>' +
                        '<li>Убить 7 Z-Lands на Outland<br>' +
                            '<span style="color: #4E4E4E;">(Засчитывается и ' +
                            'в прибрежной зоне. Горение идёт в зачёт.)</span>' +
                        '<li>5 раз нанести врагу урон гранатой<br>' +
                            '<span style="color: #4E4E4E;">(Подразумевается, ' +
                            'что в пяти боях, туз в рукаве не засчитывается. ' +
                            'Горение идёт в зачёт.)</span>' +
                        '<li>Выжить в 4 синдикатных боях<br>' +
                            '<span style="color: #4E4E4E;">(бои за бункер ' +
                            'не учитываются)</span>' +
                    '</ul></div>';

                _this.target.parentNode.setAttribute('width', '100%');
                _this.target.parentNode.nextElementSibling.
                    removeAttribute('width');
                _this.target.appendChild(span);

                var desc = general.$('questDesc');
                if (/суммарный урон.* \d+ HP/.test(desc.innerHTML)) {
                    var val = bLevel * 20;
                    desc.innerHTML = desc.innerHTML.
                        replace(/\d+ HP/, val + ' HP');
                    desc.innerHTML = desc.innerHTML.
                        replace(/\/\d+\]/, '/' + val + ']');
                }

                var questList = general.$('questList'),
                    li = questList.querySelectorAll('li'),
                    i;

                desc = /^(.*) \[/.exec(desc.innerHTML)[1];
                for (i = 0; i < li.length; i++) {
                    if (li[i].innerHTML.indexOf(desc) !== -1) {
                        li[i].setAttribute('style', 'border: #000000 1px ' +
                            'dotted; background: ' +
                            (isDone ? '#D0EED0' : '#DADADA') + ';');

                        break;
                    }
                }

                general.$('showHideQuestList').
                    addEventListener('click', function () {
                        var display = questList.style.display;
                        questList.style.display = display ? '' : 'none';
                    }, false);
            }, function () {
                general.root.setTimeout(function () {
                    _this.showQuest(url);
                }, _this.tm);
            });
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            if (this.persID && this.target) {
                this.showQuest('https://' + general.domain +
                    '/questlog.php?id=' + this.persID);
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class CommonBattleFilter {{{1
     * @constructor
     */
    var CommonBattleFilter = function () {
        /**
         * @property battleTable
         * @type {Object|null}
         */
        this.battleTable = null;

        /**
         * @method getLvl {{{2
         * @param   {Object}    row
         * @return  {int}
         */
        this.getLvl = function (row) {
            return +row.querySelector('font[color="red"]').innerHTML.
                split('-')[1];
        }; // 2}}}

        /**
         * @method sortBattleTable {{{2
         */
        this.sortBattleTable = function () {
            var stData = general.getData(20),
                row,
                i;

            for (i = 1; i < this.battleTable.rows.length; i++) {
                row = this.battleTable.rows[i];
                row.style.display = '';

                stData[0] = +stData[0];
                if (stData[0] && this.getLvl(row) > stData[0]) {
                    row.style.display = 'none';
                }

                if (stData[1] && !/<s>именные<\/s>/.test(row.innerHTML)) {
                    row.style.display = 'none';
                }

                if (stData[2] && !/по мощности/.test(row.innerHTML)) {
                    row.style.display = 'none';
                }
            }
        }; // 2}}}

        /**
         * @method getSelect {{{2
         * @param   {String}    id
         * @return  {String}
         */
        this.getSelect = function (id) {
            return '<select id="' + id + '" style="margin-right: 3px;">' +
                '<option value="0">Любой&nbsp;</option>' +
                '<option value="5">5</option>' +
                '<option value="6">6</option>' +
                '<option value="7">7</option>' +
                '<option value="8">8</option>' +
                '<option value="9">9</option>' +
                '<option value="10">10</option>' +
                '<option value="11">11</option>' +
                '<option value="12">12</option>' +
                '<option value="13">13</option>' +
                '<option value="14">14</option>' +
                '<option value="15">15</option>' +
                '<option value="16">16</option>' +
                '<option value="17">17</option>' +
                '<option value="18">18</option>' +
                '<option value="19">19</option>' +
                '<option value="20">20</option>' +
                '<option value="21">21</option>' +
                '<option value="22">22</option>' +
                '<option value="23">23</option>' +
                '<option value="24">24</option>' +
                '<option value="25">25</option>' +
                '<option value="26">26</option>' +
                '<option value="27">27</option>' +
                '<option value="28">28</option>' +
                '<option value="29">29</option>' +
                '<option value="30">30</option>' +
                '<option value="31">31</option>' +
                '<option value="32">32</option>' +
                '<option value="33">33</option>' +
                '<option value="34">34</option>' +
                '<option value="35">35</option>' +
                '<option value="36">36</option>' +
                '<option value="37">37</option>' +
                '<option value="38">38</option>' +
                '<option value="39">39</option>' +
                '<option value="40">40</option>' +
                '<option value="41">41</option>' +
                '<option value="42">42</option>' +
                '<option value="43">43</option>' +
                '<option value="44">44</option>' +
                '<option value="45">45</option>' +
                '<option value="46">46</option>' +
                '<option value="47">47</option>' +
                '<option value="48">48</option>' +
                '<option value="49">49</option>' +
                '<option value="50">50</option>' +
                '</select>';
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            // форма создания заявки
            if (/&form=\d+/.test(general.loc)) {
                return;
            }

            // основная таблица общих заявок
            this.battleTable = general.doc.querySelector('table[border="0"]' +
                '[cellpadding="5"][cellspacing="1"][style="padding-left:10px;' +
                'padding-right:10px;min-width:500px;"]');

            // сообщение о причине невозможности зайти в заявку
            // (нет оружия, недостаточно HP),  нет общих заявок или
            // таблица с заявками вообще не найдена
            if (general.doc.querySelector('table[class="panelfloat"]') ||
                    !this.battleTable ||
                    this.battleTable.rows.length === 1) {
                return;
            }

            // заявка на бой отклонена
            if (/Заявка на бой отклонена/i.test(general.doc.body.innerHTML)) {
                general.root.location.href = '/wargroup.php?war=armed';
            }

            // уже в заявке
            if (/Вы заявлены на бой/i.test(general.doc.body.innerHTML)) {
                return;
            }

            // интерфейс
            var span = general.doc.createElement('span');
            span.setAttribute('style', 'margin-left: 10px;');
            span.innerHTML = 'Максимальный уровeнь: ' +
                this.getSelect('blevel') +
                'без именных:<input type="checkbox" id="personalchk" /> ' +
                'по мощности:<input type="checkbox" id="powerchk" />';
            general.$('updatetimer2').parentNode.parentNode.appendChild(span);

            var stData = general.getData(20),
                _this = this;

            // максимальный уровень
            var blevel = general.$('blevel');
            blevel.value = stData[0];
            blevel.addEventListener('change', function () {
                var data = general.getData(20);
                data[0] = blevel.value;
                general.setData(data, 20);
                _this.sortBattleTable();
            }, false);

            // чекбокс "без именных"
            var personal = general.$('personalchk');
            personal.checked = !!stData[1];
            personal.addEventListener('click', function () {
                personal.checked = !!personal.checked;
                var data = general.getData(20);
                data[1] = personal.checked ? 1 : '';
                general.setData(data, 20);
                _this.sortBattleTable();
            }, false);

            // чекбокс "по мощности"
            var power = general.$('powerchk');
            power.checked = !!stData[2];
            power.addEventListener('click', function () {
                power.checked = !!power.checked;
                var data = general.getData(20);
                data[2] = power.checked ? 1 : '';
                general.setData(data, 20);
                _this.sortBattleTable();
            }, false);

            this.sortBattleTable();
        }; // 2}}}
    }; // 1}}}

    /**
     * @class PortTimer {{{1
     * @constructor
     */
    var PortTimer = function () {
        /**
         * @property tm
         * @type {int}
         */
        this.tm = 1200;
        /**
         * @property topPanel
         * @type {HTMLElement|null}
         */
        this.topPanel = null;
        /**
         * @property url
         * @type {String|null}
         */
        this.url = null;
        /**
         * @property date
         * @type {int}
         */
        this.date = 0;

        /**
         * @method getBattles {{{2
         */
        this.getBattles = function () {
            var _this = this;
            new AjaxQuery().init(_this.url, 'GET', null, true, function (xhr) {
                var spanContent = general.doc.createElement('span'),
                    cssSelector = 'table+table+table[cellspacing="1"]' +
                        '[cellpadding="5"][width="100%"]';

                spanContent.innerHTML = xhr.responseText;
                var table = spanContent.querySelector(cssSelector);

                if (table) {
                    var data = JSON.parse(general.getData(22)[0]),
                        trs = table.querySelectorAll('tr'),
                        nobr,
                        i;

                    data.time = [];
                    if (trs.length > 1 &&
                            !/<i>\(отсутствуют\)<\/i>/.test(trs[1].innerHTML)) {
                        for (i = 1; i < trs.length; i++) {
                            nobr = trs[i].querySelector('nobr');
                            if (nobr && /^\d+:\d+$/.test(nobr.innerHTML)) {
                                data.time.push(nobr.innerHTML);
                            }
                        }
                    }

                    data.date = _this.date;
                    data.time.reverse();
                    data.current = '';
                    general.setData(JSON.stringify(data), 22);
                    _this.setTime();
                }
            }, function () {
                general.root.setTimeout(function () {
                    _this.getBattles();
                }, _this.tm);
            });
        }; // 2}}}

        /**
         * @method resetStorage {{{2
         */
        this.resetStorage = function () {
            general.setData(JSON.stringify({
                'date': '',
                'syndid': '',
                'time': [],
                'current': ''
            }), 22);
        }; // 2}}}

        /**
         * @method getTimeDiff {{{2
         * @return  {int}
         */
        this.getTimeDiff = function () {
            var stData = JSON.parse(general.getData(22)[0]),
                now = new Date();

            stData.current = stData.current.split(':');
            // noinspection JSRemoveUnnecessaryParentheses
            return (+stData.current[0] * 60 + (+stData.current[1])) -
                ((now.getUTCHours() + 3) * 60 + now.getMinutes());
        }; // 2}}}

        /**
         * @method setTimer {{{2
         */
        this.setTimer = function () {
            var diff = this.getTimeDiff(),
                hours = parseInt((diff / 60).toString(), 10),
                min = diff - hours * 60;

            general.$('portTimer').innerHTML = (hours < 10 ? '0' + hours :
                    hours) + ':' + (min < 10 ? '0' + min : min);
        }; // 2}}}

        /**
         * @method changeCurrentTime {{{2
         */
        this.changeCurrentTime = function () {
            var stData = JSON.parse(general.getData(22)[0]);
            if (!stData.time.length) {
                stData.current = '';
                general.setData(JSON.stringify(stData), 22);
                return;
            }

            var time = stData.time.pop();
            stData.current = time;
            general.setData(JSON.stringify(stData), 22);
            if (this.getTimeDiff() <= 0) {
                this.changeCurrentTime();
                return;
            }

            this.setInterface();
            general.$('portTime').innerHTML = time;
            this.setTimer();
        }; // 2}}}

        /**
         * @method setInterface {{{2
         */
        this.setInterface = function () {
            var mainTimer = general.doc.createElement('span');
            mainTimer.innerHTML = '<a href="' + this.url +
                '" style="text-decoration: none;" target="_blank">' +
                'Порты</a> <span id="portTime" style="font-weight: ' +
                'bold;"></span> [<span id="portTimer" style=""></span>]';

            this.topPanel.appendChild(general.doc.createTextNode(' | '));
            this.topPanel.appendChild(mainTimer);
        }; // 2}}}

        /**
         * @method setTime {{{2
         */
        this.setTime = function () {
            var stData = JSON.parse(general.getData(22)[0]);
            if (!stData.current || this.getTimeDiff() <= 0) {
                this.changeCurrentTime();
            } else {
                this.setInterface();
                general.$('portTime').innerHTML = stData.current;
                this.setTimer();
            }
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            // верхняя панель
            this.topPanel = new GetTopPanel().init();
            if (!this.topPanel) {
                return;
            }

            /* localStorage:
             * [0] - '{date: '', syndid: '', time: [], current: ''}'
             */
            var stData = general.getData(22)[0];
            if (!stData) {
                this.resetStorage();
            }

            stData = JSON.parse(general.getData(22)[0]);

            // на главной странице персонажа проверяем ID основного синдиката
            if (/\/me(\/|\.php)/.test(general.loc)) {
                var linkMainSynd = general.doc.querySelector('span>b+nobr>' +
                    'a[href*="/syndicate.php?id="]');
                var syndID = linkMainSynd ?
                        /\?id=(\d+)/.exec(linkMainSynd.href)[1] : null;

                // нет основного синдиката
                if (!syndID) {
                    this.resetStorage();
                    return;
                }

                // сменили синд
                if (stData.syndid !== syndID) {
                    stData.syndid = syndID;
                    stData.time = [];
                    general.setData(JSON.stringify(stData), 22);
                }
            }

            // нет основного синдиката
            if (!stData.syndid) {
                return;
            }

            this.url = 'https://' + general.domain + '/object.php?id=11712&' +
                'page=oncoming1&sid=' + stData.syndid;

            // сегодня запрос не делали, делаем не ранее 7 утра.
            var serverHour = new Date().getUTCHours() + 3,
                now = new Date();

            serverHour = serverHour > 23 ? serverHour - 24 : serverHour;

            this.date = new Date(now.setHours(now.getHours() +
                    now.getTimezoneOffset() / 60 + 3)).getDate();

            if (+stData.date !== this.date && serverHour >= 7) {
                this.getBattles();
            } else {
                this.setTime();
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class PortsSyndLinks {{{1
     * @constructor
     */
    var PortsSyndLinks = function () {
        /**
         * @method init {{{2
         */
        this.init = function () {
            var table = general.doc.querySelector('td>table.simplewhitebg');

            if (table) {
                var syndLinks = table.querySelectorAll('a[href*="&page="]'),
                    link,
                    sign,
                    reg,
                    i;

                for (i = 0; i < syndLinks.length; i++) {
                    link = syndLinks[i];
                    reg = /&sid=(\d+)$/.exec(link.href);

                    if (reg) {
                        sign = general.doc.createElement('a');
                        sign.setAttribute('href', 'https://' + general.domain +
                            '/syndicate.php?id=' + reg[1] + '&page=online');
                        sign.setAttribute('target', '_blank');
                        sign.setAttribute('style', 'margin-right: 2px;');
                        sign.innerHTML = '<img src="https://images.' +
                            general.domain.replace('www.', '') +
                            '/img/synds/' + reg[1] + '.gif" width="20" ' +
                            'height="14" border="0" alt="img" />';

                        link.parentNode.insertBefore(sign, link);
                    }
                }
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class PersonalNPCNotifications {{{1
     * @constructor
     */
    var PersonalNPCNotifications = function () {
        /**
         * @property isCssSet
         * @type {Boolean}
         */
        this.isCssSet = false;
        /**
         * @property spanContent
         * @type {Element}
         */
        this.spanContent = general.doc.createElement('span');
        /**
         * @property ajax
         * @type {Function}
         */
        this.ajax = new AjaxQuery().init;
        /**
         * @property playSound
         * @type {Function}
         */
        this.playSound = new PlaySound().init;

        /**
         * @method setCss {{{2
         */
        this.setCss = function () {
            // css-ботва для ссылки на главную страницу NPC
            var npcLinkStyle = general.doc.createElement('style');
            npcLinkStyle.innerHTML = '@-webkit-keyframes npcBlink {' +
                    '100% { color: rgba(34, 34, 34, 0);}' +
                '}' +

                '@keyframes npcBlink {' +
                    '100% { color: rgba(34, 34, 34, 0); }' +
                '}' +

                '#npcBlink {' +
                    '-webkit-animation: npcBlink 1s linear infinite;' +
                    'animation: npcBlink 1s linear infinite;' +
                '}';

            general.doc.querySelector('head').appendChild(npcLinkStyle);
            this.isCssSet = true;
        }; // 2}}}

        /**
         * @method changepostdo {{{2
         */
        this.changepostdo = function () {
            var _this = this;
            general.root.postdo = function (url) {
                // noinspection JSUnresolvedFunction
                var url_loaded = url,
                    my_main_div = $('#my_main_div');

                // noinspection JSUnresolvedFunction
                my_main_div.css('opacity', '0.6');

                /*jslint unparam: true */
                /*eslint no-unused-vars: 0 */
                // noinspection JSUnusedLocalSymbols
                my_main_div.load(url,
                    function (responseTxt, statusTxt, xhr) {
                        if (statusTxt === 'success') {
                            // noinspection JSUnresolvedFunction
                            $('#my_main_div').css('opacity', '1');
                            window.history.
                                replaceState({}, null, url_loaded);
                            _this.init();
                        } else {
                            // noinspection JSUnresolvedFunction
                            $('#my_main_div').css('opacity', '0.3');
                            window.location.href = url_loaded;
                        }
                    });

                return false;
            };
        }; // 2}}}

        /**
         * @method start {{{2
         */
        this.start = function () {
            var npcLink = general.doc.querySelector('a[href*="/me.php?nid="]' +
                '[onclick^="dolink"]');

            if (npcLink) {
                var url = 'https://' + general.domain + '/info.php?id=' +
                        /\?nid=(\d+)/.exec(npcLink.href)[1],
                    _this = this;

                this.ajax(url, 'GET', null, true, function (xhr) {
                    _this.spanContent.innerHTML = xhr.responseText;
                    var link = _this.spanContent.
                            querySelector('a[onclick^="show_npc_control"]'),
                        div = _this.spanContent.
                            querySelector('#namespan').parentNode,
                        health = /\[(\d+) \/ (\d+)\]/.exec(div.innerHTML);

                    // noinspection JSRemoveUnnecessaryParentheses
                    health = Math.floor(+health[1] * 100 / (+health[2]));

                    if (link.innerHTML === 'Ожидает распоряжений' &&
                            health >= 80) {
                        npcLink.setAttribute('id', 'npcBlink');
                        _this.playSound(general.getData(18)[0]);
                    } else if (link.innerHTML === 'Путешествует по Аутленду'
                            && health < 30) {
                        npcLink.setAttribute('style', 'background: #FFE3E3');
                        _this.playSound(general.getData(18)[1]);
                    } else {
                        npcLink.removeAttribute('id');
                        npcLink.removeAttribute('style');
                    }

                    general.root.setTimeout(function () {
                        _this.start();
                    }, 10000);
                }, function () {
                    general.root.setTimeout(function () {
                        _this.start();
                    }, 3000);
                });
            }
        }; // 2}}}

        /**
         * @method init {{{2
         */
        this.init = function () {
            if (!this.isCssSet) {
                this.setCss();
            }

            this.changepostdo();
            this.start();
        }; // 2}}}
    }; // 1}}}

    /**
     * @class BbCodeInMessages {{{1
     * @constructor
     */
    var BbCodeInMessages = function () {
        /**
         * @property textArea
         * @type {Object|null}
         */
        this.textArea = general.doc.querySelector('textarea[name="msg"],' +
                'textarea[name="message"]') ||
                    general.doc.querySelector('textarea#newsms');

        /**
         * @method  setButton {{{2
         * @param   {String}    imgName
         * @param   {String}    title
         * @param   {Object}    target
         * @param   {String}    tag
         */
        this.setButton = function (imgName, title, target, tag) {
            var button = general.doc.createElement('span');
            button.setAttribute('style', 'margin-left: 10px; cursor: pointer;');
            button.innerHTML = '<img src="' + general.imgPath +
                'BbCodeInMessages/' + imgName +
                '.png" width="14" height="14" title="' + title + '" ' +
                'alt="' + title + '">';
            target.appendChild(button);

            var _this = this;
            button.addEventListener('click', function () {
                var text = _this.textArea.value,
                    cursorPos = _this.textArea.selectionStart;

                _this.textArea.value = text.substring(0, cursorPos) + tag +
                    text.substring(cursorPos, text.length);
                _this.textArea.focus();
                _this.textArea.selectionEnd = cursorPos + 3;
            }, false);
        }; // 2}}}

        /**
         * @method  init {{{2
         */
        this.init = function () {
            var sendButton = general.doc.
                    querySelector('input[value="Отправить сообщение"]');

            if (this.textArea) {
                var target;
                if (sendButton) { // старая версия почты и форум
                    target = sendButton.parentNode.parentNode.parentNode.
                            parentNode.querySelectorAll('td');

                    target = /sms-create/.test(general.loc) ? target[4] :
                            target[0];
                } else {
                    target = general.doc.
                        querySelector('td>span[style="opacity:0.8;"]');
                }

                if (target) {
                    this.setButton('quote', 'Цитирование', target, '[q][/q]');
                    this.setButton('italic', 'Наклонный шрифт', target,
                        '[i][/i]');
                }
            }
        }; // 2}}}
    }; // 1}}}

    /**
     * @class ProfessionLevels {{{1
     * @constructor
     */
    var ProfessionLevels = function () {
        /**
         * @property profLevels
         * @type {Array}
         */
        this.profLevels = [51, 109, 184, 284, 422, 609, 856, 1176, 1581, 2085];

        /**
         * @method  setCounter {{{2
         * @param   {Object}    target
         * @param   {int}       value
         */
        this.setCounter = function (target, value) {
            var td = general.doc.createElement('td');
            td.setAttribute('style', 'padding-left: 5px; font-size: 10px; ' +
                'color: #809980;');
            td.innerHTML = '+' + value;
            target.appendChild(td);
        }; // 2}}}

        /**
         * @method  init {{{2
         */
        this.init = function () {
            var killer = general.doc.querySelector('table[border="0"] ' +
                    'td[style*="font-size:10px"]');

            if (!killer) {
                return;
            }

            var profsTrs = killer.parentNode.parentNode.querySelectorAll('tr'),
                currentVal,
                i,
                j;

            for (i = 0; i < 3; i++) {
                currentVal = /\((\d+(\.\d+)?)\)/.exec(profsTrs[i].innerHTML)[1];
                currentVal = Math.floor(currentVal);
                for (j = 0; j < this.profLevels.length; j++) {
                    if (currentVal < this.profLevels[j]) {
                        this.setCounter(profsTrs[i],
                            this.profLevels[j] - currentVal);
                        break;
                    }
                }
            }

            var tr = general.doc.createElement('tr');
            tr.innerHTML = '<td colspan="3"><a target="_blank" ' +
                'style=" font-size: 10px;" href="http://www.ganjawiki.ru/' +
                'Очки_опыта#Таблица_опыта_профессий">Таблица опыта ' +
                'профессий</a></td>';
            profsTrs[0].parentNode.appendChild(tr);
        }; // 2}}}
    }; // 1}}}

    general = new General();

    // не в игре
    if ((general.doc.querySelector('a[href$="/login.php"]') ||
            general.doc.querySelector('a[href$="/regform.php"]')) &&
                !/\/news\.php/.test(general.loc)) {
        return;
    }

    if (!general.checkMainData()) {
        return;
    }

    var head = general.doc.querySelector('head');
    if (!head) {
        return;
    }

    // main init {{{1
    initScript = general.getInitScript();

    // на ganjafoto, ganjafile или на ауте меняем фавикон
    if (/ganjafoto|ganjafile|photos|quest\.gwars/.test(general.loc)) {
        try {
            new NotGiveCannabisLeaf().init();
        } catch (e) {
            general.cons.log(e);
        }

        return;
    }

    // везде на www.gwars
    if (initScript[0]) {
        try {
            new NotGiveCannabisLeaf().init();
        } catch (e) {
            general.cons.log(e);
        }
    }

    if (initScript[36]) {
        try {
            new PortTimer().init();
        } catch (e) {
            general.cons.log(e);
        }
    }

    if (initScript[52]) {
        try {
            new SoundSyndBattle().init();
        } catch (e) {
            general.cons.log(e);
        }
    }

    // везде кроме фермы
    if (!/\/ferma\.php/.test(general.loc)) {
        if (initScript[41]) {
            try {
                new ScanPers().init();
            } catch (e) {
                general.cons.log(e);
            }
        }

        if (initScript[4]) {
            try {
                new BlacklistHighlighting().init();
            } catch (e) {
                general.cons.log(e);
            }
        }
    }

    // везде кроме боев
    if (!/\/b0\//.test(general.loc)) {
        try {
            new SetSettingsButton().init();
        } catch (e) {
            general.cons.log(e);
        }

        if (/\/news\.php\?set=1/.test(general.loc)) {
            try {
                new ShowMainSettings().init();
            } catch (e) {
                general.cons.log(e);
            }
        }

        if (/\/sms\.php/.test(general.loc)) {
            if (initScript[8]) {
                try {
                    new DeleteSms().init();
                } catch (e) {
                    general.cons.log(e);
                }
            }
        }

        if (/\/sms-read\.php\?type=/.test(general.loc)) {
            if (initScript[26]) {
                try {
                    new HistorySms().init();
                } catch (e) {
                    general.cons.log(e);
                }
            }

            if (/\?type=1&id=/.test(general.loc)) {
                if (initScript[54]) {
                    try {
                        new DelAndAddBlackSms().init();
                    } catch (e) {
                        general.cons.log(e);
                    }
                }
            }
        }

        if (/\/ferma\.php/.test(general.loc)) {
            if (initScript[9]) {
                try {
                    new FarmExperience().init();
                } catch (e) {
                    general.cons.log(e);
                }
            }

            if (initScript[13]) {
                try {
                    new AllPlantsOnFarm().init();
                } catch (e) {
                    general.cons.log(e);
                }
            }
        }

        if (initScript[10]) {
            try {
                new FarmTimer().init();
            } catch (e) {
                general.cons.log(e);
            }
        }

        if (initScript[14]) {
            try {
                new GwMenu().init();
            } catch (e) {
                general.cons.log(e);
            }
        }

        if (initScript[1]) {
            try {
                new AdditionForNavigationBar().init();
            } catch (e) {
                general.cons.log(e);
            }
        }

        if (initScript[6]) {
            try {
                new ResourcesAndBonuses().init();
            } catch (e) {
                general.cons.log(e);
            }
        }

        if (!/\/ferma\.php/.test(general.loc)) {
            if (initScript[47]) {
                try {
                    new ShowMyAchievements().init();
                } catch (e) {
                    general.cons.log(e);
                }
            }

            if (/\/me(\/|\.php)|\/(warlog|warlist|wargroup)\.php\?/.
                    test(general.loc)) {
                if (initScript[5]) {
                    try {
                        new WorkPostGrenadesBroken().init();
                    } catch (e) {
                        general.cons.log(e);
                    }
                }
            }

            if (initScript[43]) {
                try {
                    new SearchUser().init();
                } catch (e) {
                    general.cons.log(e);
                }
            }
        }

        if (/\/me(\/|\.php)|\/npc\.php\?id=/.test(general.loc)) {
            if (initScript[12]) {
                try {
                    new TimeNpc().init();
                } catch (e) {
                    general.cons.log(e);
                }
            }

            if (/\/me(\/|\.php)/.test(general.loc)) {
                if (initScript[17]) {
                    try {
                        new GbCounter().init();
                    } catch (e) {
                        general.cons.log(e);
                    }
                }

                if (initScript[44]) {
                    try {
                        new SkillCounters().init();
                    } catch (e) {
                        general.cons.log(e);
                    }
                }

                if (initScript[49]) {
                    try {
                        new SyndOnlineOnMainPage().init();
                    } catch (e) {
                        general.cons.log(e);
                    }
                }

                if (initScript[56]) {
                    try {
                        new Regeneration().init();
                    } catch (e) {
                        general.cons.log(e);
                    }
                }

                if (initScript[35]) {
                    try {
                        new PersonalNPCNotifications().init();
                    } catch (e) {
                        general.cons.log(e);
                    }
                }
            }
        }

        if (/\/items\.php/.test(general.loc)) {
            if (initScript[15]) {
                try {
                    new InventoryPlus().init();
                } catch (e) {
                    general.cons.log(e);
                }
            }
        }

        if (/\/info.warstats\.php\?id=/.test(general.loc)) {
            if (initScript[16]) {
                try {
                    new CountBattles().init();
                } catch (e) {
                    general.cons.log(e);
                }
            }
        }

        if (/\/shopc\.php|\/market-p\.php\?stage=2&item_id=/.
                test(general.loc)) {
            if (initScript[19]) {
                try {
                    new BuyHightech().init();
                } catch (e) {
                    general.cons.log(e);
                }
            }
        }

        if (/\/shop\.php/.test(general.loc)) {
            if (initScript[27]) {
                try {
                    new LinksToHighTech().init();
                } catch (e) {
                    general.cons.log(e);
                }
            }
        }

        if (/(messages|sms-(create|read|chat)).php/.test(general.loc)) {
            if (initScript[62]) {
                try {
                    new BbCodeInMessages().init();
                } catch (e) {
                    general.cons.log(e);
                }
            }
        }

        if (/\/me(\/|\.php)|\/messages\.php\?fid=1&tid=/.
                test(general.loc)) {
            if (initScript[20]) {
                try {
                    new NewsAndInvit().init();
                } catch (e) {
                    general.cons.log(e);
                }
            }
        }

        if (/\/(messages|threads)\.php\?fid=/.test(general.loc) ||
                /\/forum.php$/.test(general.loc)) {
            if (initScript[53]) {
                try {
                    new AdvForum().init();
                } catch (e) {
                    general.cons.log(e);
                }
            }

            if (/\/messages\.php/.test(general.loc)) {
                if (initScript[42]) {
                    try {
                        new ShowInitMessOnForum().init();
                    } catch (e) {
                        general.cons.log(e);
                    }
                }
            }
        }

        if (/\/stats\.php$/.test(general.loc)) {
            if (initScript[22]) {
                try {
                    new FilterResOnStat().init();
                } catch (e) {
                    general.cons.log(e);
                }
            }
        }

        if (/\/me(\/|\.php)|\/info\.php\?id=/.test(general.loc)) {
            if (initScript[24]) {
                try {
                    new FixSkills().init();
                } catch (e) {
                    general.cons.log(e);
                }
            }

            if (initScript[25]) {
                try {
                    new FuckTheFarm().init();
                } catch (e) {
                    general.cons.log(e);
                }
            }

            if (/\/info\.php\?id=/.test(general.loc)) {
                if (initScript[28]) {
                    try {
                        new GameMania().init();
                    } catch (e) {
                        general.cons.log(e);
                    }
                }

                if (initScript[38]) {
                    try {
                        new RangeWeapon().init();
                    } catch (e) {
                        general.cons.log(e);
                    }
                }

                if (initScript[40]) {
                    try {
                        new ScanKarma().init();
                    } catch (e) {
                        general.cons.log(e);
                    }
                }

                if (initScript[57]) {
                    try {
                        new ProfColor().init();
                    } catch (e) {
                        general.cons.log(e);
                    }
                }

                if (initScript[58]) {
                    try {
                        new CurrentQuestOnInfo().init();
                    } catch (e) {
                        general.cons.log(e);
                    }
                }

                if (initScript[63]) {
                    try {
                        new ProfessionLevels().init();
                    } catch (e) {
                        general.cons.log(e);
                    }
                }
            }
        }

        if (/\/info\.realty\.php\?id=2$/.test(general.loc)) {
            if (initScript[29]) {
                try {
                    new GosEnergoAtomFilter().init();
                } catch (e) {
                    general.cons.log(e);
                }
            }
        }

        if (/\/syndicate\.php\?id=/.test(general.loc)) {
            if (initScript[30]) {
                try {
                    new SortSyndOnline().init();
                } catch (e) {
                    general.cons.log(e);
                }
            }

            if (initScript[45]) {
                try {
                    new SyndPtsAnalyser().init();
                } catch (e) {
                    general.cons.log(e);
                }
            }

            if (initScript[46]) {
                try {
                    new SyndAnalyser().init();
                } catch (e) {
                    general.cons.log(e);
                }
            }
        }

        if (/\/map.php/.test(general.loc)) {
            if (initScript[37]) {
                try {
                    new PortsAndTerminals().init();
                } catch (e) {
                    general.cons.log(e);
                }
            }
        }

        if (/\/home\.senditem\.php/.test(general.loc)) {
            if (initScript[39]) {
                try {
                    new RentAndSale().init();
                } catch (e) {
                    general.cons.log(e);
                }
            }
        }

        if (/\/info\.php\?id=|\/info\.vote\.php\?id=/.test(general.loc)) {
            if (initScript[50]) {
                try {
                    new TimeKarma().init();
                } catch (e) {
                    general.cons.log(e);
                }
            }
        }

        if (/\/object\.php\?id=/.test(general.loc)) {
            if (initScript[61]) {
                try {
                    new PortsSyndLinks().init();
                } catch (e) {
                    general.cons.log(e);
                }
            }
        }
    }

    // бои
    if (/(\/b0\/|\/(wargroup|warlist|warlog|battlelog|duels)\.php)/.
            test(general.loc)) {

        if (initScript[3]) {
            try {
                new AdvBattleAll().init();
            } catch (e) {
                general.cons.log(e);
            }
        }

        if (/(\/b0\/|\/(warlog|battlelog)\.php)/.test(general.loc)) {
            if (initScript[7]) {
                try {
                    new CritShotsAndLinksBtlLog().init();
                } catch (e) {
                    general.cons.log(e);
                }
            }

            if (initScript[31]) {
                try {
                    new HousHealth().init();
                } catch (e) {
                    general.cons.log(e);
                }
            }

            if (initScript[51]) {
                try {
                    new ImgPokemonsOnBattle().init();
                } catch (e) {
                    general.cons.log(e);
                }
            }
        }

        if (/\/warlist\.php/.test(general.loc)) {
            if (initScript[23]) {
                try {
                    new FilterWarlistOne2One().init();
                } catch (e) {
                    general.cons.log(e);
                }
            }

            if (initScript[34]) {
                try {
                    new One2OneCallerInfo().init();
                } catch (e) {
                    general.cons.log(e);
                }
            }

            if (/\?war=(armed|duels)/.test(general.loc)) {
                if (initScript[33]) {
                    try {
                        new LinksInOne2One().init();
                    } catch (e) {
                        general.cons.log(e);
                    }
                }
            }
        }

        if (/\/wargroup\.php\?war=armed/.test(general.loc)) {
            if (initScript[59]) {
                try {
                    new CommonBattleFilter().init();
                } catch (e) {
                    general.cons.log(e);
                }
            }
        }
    } // 1}}}

}());

// vim:fdm=marker