// JavaScript Document

var LBJStatsLoader = Class.extend({

    STATS_LOADER_URL: './r/s',
    
    LOCK_IMG_SRC: './img/lock-2.png',
    JOIN_IMG_SRC: './img/action-join.jpg',
    LEAVE_IMG_SRC: './img/action-leave.jpg',
    NOACT_IMG_SRC: './img/action-none.jpg',
    BACK_BTN_SRC: './img/page-back.jpg',
    FWD_BTN_SRC: './img/page-fwd.jpg',
    
    USER_TABLE_CAPTION: 'Группы, в которых вы учавствуете:',
    USER_TABLE_HEADERS: { 'gid': 'Группа', 
                          '.users': 'Участники', 
                          'ucount': 'Кол-во',
                          '.cards': 'Карты', 
                          'score': 'Сумма', 
                          '.isprotected': 'Защита',
                          '.leave': '-' // '&minus;'
                        },
    
    GROUPS_TABLE_CAPTION: 'Все группы:',
    GROUPS_TABLE_HEADERS: { 'gid': 'Группа', 
                            '.users': 'Участники',
                            'ucount': 'Кол-во',
                            '.cards': 'Карты', 
                            'score': 'Сумма',
                            '.creator': 'Создатель',
                            '.isprotected': 'Защита',
                            '.join': '+', 
                            '.leave': '-' // '&minus;'
                          },    
    
    USERS_TABLE_CAPTION: 'Все участники:',
    USERS_TABLE_HEADERS: { 'lid': 'Имя', 
                           'cardid': 'Карта', 
                           'gscore': 'Очки', 
                           'gcount': 'Кол-во групп',
                           '.groups': 'Группы' },
                           
    CARD_INFO_FIELDS: { html: 'Символика',
                        name: 'Название',                        
                        score: 'Величина'                        
                      },                      
    USER_INFO_FIELDS: { uname: 'Никнейм',
                        uid: 'ID',
                        fcard: 'Карта',                
                        gscore: 'Заслуги',                        
                        grcount: 'Кол-во групп',
                        fgroups: 'Входит в',
                        frtime: 'Зарегистрирован',
                        pos: 'Позиция'
                      },
    GROUP_INFO_FIELDS: { name: 'Название',
                         creator: 'Создатель',                
                         score: 'Сумма очков',                        
                         fprotected: 'Защита',
                         fusers: 'Участники',
                         fucount: 'Кол-во участников',
                         fctime: 'Время создания',
                         pos: 'Позиция'
                      },                      
    GCOUNT_INFO_FIELDS: {
                        fgroups: 'Группы'
                      },
    UCOUNT_INFO_FIELDS: {
                        fusers: 'Участники'
                      },
    GSCORE_INFO_FIELDS: {
                        fcards: 'Карты'
                      },                      
                      
    TIME_FORMAT: '%day% %month% %year%, %wday% %hour%%min%.%sec%',

    TIME_MONTHS: ['Jan', 'Feb', 
                  'Mar', 'Apr', 'May', 
                  'Jun', 'Jul', 'Aug', 
                  'Sep', 'Oct', 'Nov', 
                  'Dec'],
    TIME_WDAYS: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],  
    
    PAGE_NAV_CLASS: 'pageNav',
    PAGE_NAV_WIDTH: 10,
    
    construct:
        function(userId) {
            this._currentUser = userId;
            
            this._infoBlock = null;
            this._floatingInfoElm = null;
            
            this._userClickHandler = null;
            this._groupClickHandler = null;
            
            this._groupJoinClickHandler = null;
            this._groupLeaveClickHandler = null;
            
            this._statsUpdatedHandler = null;
            
            this._userTableStatus = {
                    target: 'user',
                    offset: 0,
                    rows: 15,
                    orderBy: 'gid',
                    orderAsc: true,
                    navPos: 0
                };            
            this._groupsTableStatus = {
                    target: 'groups',
                    offset: 0,
                    rows: 25,
                    orderBy: 'score',
                    orderAsc: true,
                    navPos: 0
                };
            this._usersTableStatus = {
                    target: 'users',
                    offset: 0,
                    rows: 25,
                    orderBy: 'gscore',
                    orderAsc: false,
                    navPos: 0
                };
            this._tablesElems = {};
            
            this._loadingDisplayElm = null;
            this._loadingProcessesShowing = 0;                      
        },
        
    initializeInfoElems:
        function(infoCardElmId, floatingInfoElmId) {
            this._infoCard = document.getElementById(infoCardElmId);
            this._floatingInfoElm = document.getElementById(floatingInfoElmId);
        },
        
    getInfoCardElm:
        function() {
        	return this._infoCard;
        },
        
    setCurrentUser:
        function(userId) {
            this._currentUser = userId;
        },
        
    _refreshTable:
        function(tableData) {
            var callBackMethod = null;            
            switch (tableData.target) {
                case 'user': callBackMethod = createMethodReference(this, "onUserStatsReceived"); break;
                case 'groups': callBackMethod = createMethodReference(this, "onGroupsStatsReceived"); break;
                case 'users': callBackMethod = createMethodReference(this, "onUsersStatsReceived"); break;
            }
            var reqParams =  'target=' + tableData.target + 
                            '&userId=' + (this._currentUser ? urlEncode(this._currentUser) : '') +
                            '&rows=' + tableData.rows +
                            '&offset=' + tableData.offset +
                            '&oby=' + tableData.orderBy +
                            '&oasc=' + tableData.orderAsc;
            this._showLoadingStatus();
            makeRequest(this.STATS_LOADER_URL, reqParams, callBackMethod, true, this._tablesElems[tableData.target]);
        },
        
    refreshAllTables:
        function() {
            if (this._currentUser) this._refreshTable(this._userTableStatus);
            this._refreshTable(this._groupsTableStatus);
            this._refreshTable(this._usersTableStatus);
        },
        
    loadUserStats:
        function(tableElmId) {
            this._tablesElems['user'] = tableElmId;
            if (this._currentUser) {
                this._refreshTable(this._userTableStatus);
            }
        },
        
    loadGroupsStats:
        function(tableElmId) {
            this._tablesElems['groups'] = tableElmId;
            this._refreshTable(this._groupsTableStatus);        
        },
        
    loadUsersStats:
        function(tableElmId) {
            this._tablesElems['users'] = tableElmId;
            this._refreshTable(this._usersTableStatus);        
        },
        
    loadGroupInfo:
        function(groupId) { 
            // makeRequest(this.STATS_LOADER_URL, 'target=ginfo&gid=' + groupId, gotGroupInfo);        
        },
    
    loadUserInfo:
        function(userId) {
            // makeRequest(this.STATS_LOADER_URL, 'target=uinfo&uid=' + userId, gotUserInfo);        
        }, 
        
    _isCorrectResponse:
        function(response) {
            return ((response != ':(') && (response != '":("'));
        },
        
    setStatsUpdatedHandler:
        function(handler) {
        	this._statsUpdatedHandler = handler;
        },
        
    onUserStatsReceived:
        function(request, response, tableElmId) {
            if (this._isCorrectResponse(response)) {
                this._buildGroupsStats(tableElmId, (typeof(response) == 'string') ? JSON.parse(response) : response, true);            
            } else {
                alert('Не удалось получить данные с сервера для таблицы ' + tableElmId + '. Ну не удалось.');
            };
            this._hideLoadingStatus();
            if (this._statsUpdatedHandler) this._statsUpdatedHandler();
        },
        
    onGroupsStatsReceived:
        function(request, response, tableElmId) {
            if (this._isCorrectResponse(response)) {
                this._buildGroupsStats(tableElmId, (typeof(response) == 'string') ? JSON.parse(response) : response);
            } else {
                alert('Не удалось получить данные с сервера для таблицы ' + tableElmId + '. Ну не удалось.');
            };
            this._hideLoadingStatus();
            if (this._statsUpdatedHandler) this._statsUpdatedHandler();
        },
        
    onUsersStatsReceived:
        function(request, response, tableElmId) {
            if (this._isCorrectResponse(response)) {
                this._buildUsersStats(tableElmId, (typeof(response) == 'string') ? JSON.parse(response) : response);
            } else {
                alert('Не удалось получить данные с сервера для таблицы ' + tableElmId + '. Ну не удалось.');
            };
            this._hideLoadingStatus();
            if (this._statsUpdatedHandler) this._statsUpdatedHandler();
        },
        
    _buildGroupsStats:
        /* { 'gid': 'Группа', 
             '.users': 'Участники',
             'ucount': 'Кол-во',
             '.cards': 'Карты', 
             'score': 'Сумма очков', 
             '.join': '+', 
             '.leave': '&minus;'
        }, */
        function(tableElmId, groupsData, singleUserMode) {
            var tableElm = document.getElementById(tableElmId);
            if (tableElm) {
                // tableElm.innerHTML = '';
                tableElm = clearContents(tableElm);
                this._buildTableHeader( singleUserMode ? {
                    elm: tableElm, 
                    headers: this.USER_TABLE_HEADERS, 
                    caption: this.USER_TABLE_CAPTION, 
                    status: this._userTableStatus,
                    count: groupsData.count
                } : {
                    elm: tableElm, 
                    headers: this.GROUPS_TABLE_HEADERS, 
                    caption: this.GROUPS_TABLE_CAPTION, 
                    status: this._groupsTableStatus,
                    count: groupsData.count   
                } );
                var tbodyElm = document.createElement('tbody');
                if (parseInt(groupsData.count) == 0) { 
                    tbodyElm.appendChild(this._makeEmptyBodyInner(
                        singleUserMode ? objPropsCount(this.USER_TABLE_HEADERS) : objPropsCount(this.GROUPS_TABLE_HEADERS)
                    ));
                }           
                for (var groupIdx in groupsData.groups) {
                    var rowElm = document.createElement('tr');
                    var group = groupsData.groups[groupIdx]; 
                    var gnameElm = document.createElement('td');
                    gnameElm.appendChild(this._decorateGroupName(group.name, group));
                    rowElm.appendChild(gnameElm);
                    var ulistElm = document.createElement('td');
                    for(var userIdx in group.users) {
                        var user = group.users[userIdx];
                        ulistElm.appendChild(this._decorateUserName(user.uname, user));
                        if (userIdx != (group.users.length - 1)) ulistElm.appendChild(document.createTextNode(' ')); 
                    }
                    rowElm.appendChild(ulistElm);
                    var ucountElm = document.createElement('td');
                    ucountElm.appendChild(this._decorateUsersCount(group.ucount, group.users));
                    rowElm.appendChild(ucountElm);
                    var cardsData = [];
                    var clistElm = document.createElement('td');
                    for(var userIdx in group.users) {
                        var user = group.users[userIdx];
                        clistElm.appendChild(this._decorateCard(user.card));
                        cardsData.push(user.card);
                        if (userIdx != (group.users.length - 1)) clistElm.appendChild(document.createTextNode(' ')); 
                    }
                    rowElm.appendChild(clistElm);
                    var scoreElm = document.createElement('td');
                    scoreElm.appendChild(this._decorateGScore(group.score, cardsData));
                    rowElm.appendChild(scoreElm);                    
                    if (!singleUserMode) {
                        var creatorElm = document.createElement('td');
                        creatorElm.appendChild((group.users.length > 0) ? this._decorateUserName(group.creator, group.users[0]) : document.createTextNode('-'));
                        rowElm.appendChild(creatorElm);
                    }                    
                    var protectedElm = document.createElement('td');
                    protectedElm.appendChild(this._decorateProtectionValue(group.isprotected));
                    rowElm.appendChild(protectedElm);
                    if (this._currentUser) {
                        if (!singleUserMode) {
                            var joinButton = document.createElement('td');
                            joinButton.appendChild(this._decorateGJoinButton(group.name, group, group.ajoin));
                            rowElm.appendChild(joinButton);
                        }
                        var leaveButton = document.createElement('td');
                        leaveButton.appendChild(this._decorateGLeaveButton(group.name, group, group.aleave));
                        rowElm.appendChild(leaveButton);
                    }
                    if (this._groupClickHandler) rowElm.onclick = createMethodReference_(this, '_groupClickHandler', group);
                    rowElm.onmouseover = function() { this.id = 'mouse-over' };
                    rowElm.onmouseout = function() { this.id = '' };                    
                    tbodyElm.appendChild(rowElm);                                        
                }
                tableElm.appendChild(tbodyElm);
            } else alert('Элемент ' +  tableElmId + 'не найден!');        
        },    
        
    _buildUsersStats:
        function(tableElmId, usersData) {
            var tableElm = document.getElementById(tableElmId);
            if (tableElm) {
                // tableElm.innerHTML = '';
                tableElm = clearContents(tableElm);
                this._buildTableHeader( {
                    elm: tableElm, 
                    headers: this.USERS_TABLE_HEADERS, 
                    caption: this.USERS_TABLE_CAPTION, 
                    status: this._usersTableStatus,
                    count: usersData.count   
                } );
                var tbodyElm = document.createElement('tbody');
                if (usersData.count == 0) {
                    tbodyElm.appendChild(this._makeEmptyBodyInner(objPropsCount(this.USERS_TABLE_HEADERS)));
                }                 
                for (var userIdx in usersData.users) {
                    var rowElm = document.createElement('tr');
                    var user = usersData.users[userIdx]; 
                    var unameElm = document.createElement('td');
                    unameElm.appendChild(this._decorateUserName(user.uname, user));
                    rowElm.appendChild(unameElm);
                    var cardElm = document.createElement('td');
                    cardElm.appendChild(this._decorateCard(user.card));
                    rowElm.appendChild(cardElm);
                    var scoreElm = document.createElement('td');
                    scoreElm.appendChild(this._decorateUScore(user.gscore, user.groups));
                    rowElm.appendChild(scoreElm);
                    var grcntElm = document.createElement('td');
                    grcntElm.appendChild(this._decorateGroupsCount(user.grcount, user.groups));
                    rowElm.appendChild(grcntElm);
                    var glistElm = document.createElement('td');
                    var groupsLength = user.groups.length;
                    var limit = (groupsLength < 9) ? groupsLength : 9; 
                    for (var groupIdx = 0; groupIdx < limit; groupIdx++) {
                    // for(var groupIdx in user.groups) {
                        var group = user.groups[groupIdx];
                        glistElm.appendChild(this._decorateGroupName(group.name, group));
                        if (groupIdx != (user.groups.length - 1)) glistElm.appendChild(document.createTextNode(' ')); 
                    }
                    if (groupsLength > 9) {
                    	var hellipSpan = document.createElement('span');
                    	hellipSpan.innerHTML = '&hellip;';
                    	hellipSpan.className = 'hellip';
                        glistElm.appendChild(hellipSpan);
                    }
                    rowElm.appendChild(glistElm);                   
                    if (this._userClickHandler) rowElm.onclick = createMethodReference_(this, '_userClickHandler', user);
                    rowElm.onmouseover = function() { this.id = 'mouse-over' };
                    rowElm.onmouseout = function() { this.id = '' };
                    tbodyElm.appendChild(rowElm);                                        
                }
                tableElm.appendChild(tbodyElm);
            } else alert('Элемент ' +  tableElmId + 'не найден!');
        },
        
     _buildTableHeader:
        /* function(tableElm, tableHeaders, tableCaption, tableData) { */
        function(tableOptions) {
            var tableElm = tableOptions.elm;
            
            var captionElm = document.createElement('caption');
            captionElm.appendChild(this._makePageButton(false, tableOptions.status, tableOptions.count));            
            captionElm.appendChild(document.createTextNode(tableOptions.caption));            
            captionElm.appendChild(document.createTextNode(' '));            
            captionElm.appendChild(this._decorateCountPads(tableOptions.count,
                                                           tableOptions.status.rows,
                                                           tableOptions.status.offset 
                                                          ));
            captionElm.appendChild(this._makePageButton(true, tableOptions.status, tableOptions.count));
            captionElm.appendChild(this._createPageNavigator(tableOptions.status, tableOptions.count,
                                                           tableOptions.status.rows,
                                                           tableOptions.status.offset));
            
            tableElm.appendChild(captionElm);
            var theadElm = document.createElement('thead');
            var theadRowElm = document.createElement('tr');
            var tableHeaders = tableOptions.headers;
            for (var headerIdx in tableHeaders) {
                var theadCellElm = document.createElement('th');
                theadCellElm.appendChild(
                    this._decorateColumnHead(tableHeaders[headerIdx], headerIdx, tableOptions.status));
                theadRowElm.appendChild(theadCellElm);
            }
            theadElm.appendChild(theadRowElm)
            tableElm.appendChild(theadElm);
        },
        
    _makeEmptyBodyInner:
        function(cellsCount) {
            var textRow = document.createElement('tr');
            var textCell = document.createElement('td');
            textCell.colSpan = cellsCount;            
            textCell.className = 'no-data';            
            var textSpan =  document.createElement('span');
            textSpan.appendChild(document.createTextNode('Количественная характеристика данных идентична нулю'));
            textCell.appendChild(textSpan);
            textRow.appendChild(textCell) 
            return textRow;
        },
        
    _decorateColumnHead:
        function(columnName, columnField, tableData) {
            if (columnField && columnField[0] != '.') {
                var isActiveColumn = (tableData.orderBy == columnField);
                var columnLink = document.createElement('a');
                columnLink.href = '#';
                columnLink.title = columnName;
                columnLink.alt = columnName;
                columnLink.className = 'column-head ' + (isActiveColumn ? 'active-column ' : '') + (tableData.orderAsc ? 'ascending' : 'descending');            
                var statsBuilder = this;                 
                //var methRef = createMethodReference_(this, '_refreshTable', tableData);         
                columnLink.onclick = function() {
                    tableData.offset = 0;
                    tableData.orderBy = columnField;
                    tableData.orderAsc = !tableData.orderAsc;
                    this.className = 'column-head ' + (isActiveColumn ? 'active-column ' : '') + (tableData.orderAsc ? 'ascending' : 'descending');
                    if (this.lastChild) this.lastChild.innerHTML = tableData.orderAsc ? '&darr;' : '&uarr;';  
                    statsBuilder._refreshTable(tableData);
                    return false;
                }
                columnLink.appendChild(document.createTextNode(columnName));
                var orderStatusElm = document.createElement('span');
                orderStatusElm.innerHTML = (isActiveColumn) ? (tableData.orderAsc ? '&darr;' : '&uarr;') : ':';
                columnLink.appendChild(orderStatusElm);
                return columnLink;
            } else return document.createTextNode(columnName);
        },
        
    _decorateCountPads:
        function(totalNum, packSize, curOffset) {
            var coveringSpan = document.createElement('span');
            coveringSpan.appendChild(document.createTextNode('('));
            var itemsCountSpan = document.createElement('span');
            itemsCountSpan.className = 'item-count'; 
            if (totalNum != 0) {
            	var firstOnThisPage = curOffset + 1;
            	var lastOnThisPage = ((curOffset + packSize) < totalNum) ? (curOffset + packSize) : totalNum;
                itemsCountSpan.appendChild(document.createTextNode(firstOnThisPage + "-" + lastOnThisPage + " из " + totalNum));
            } else {
                itemsCountSpan.appendChild(document.createTextNode("0-0 из 0"));
            }
            coveringSpan.appendChild(itemsCountSpan);
            coveringSpan.appendChild(document.createTextNode(':'));
            var pagesCountSpan = document.createElement('span');
            pagesCountSpan.className = 'page-count';
            var pageNum = div(curOffset, packSize).q + 1;
            var pagesCount = (totalNum != 0) ? div_(totalNum, packSize) : 1;
            pagesCountSpan.appendChild(document.createTextNode(pageNum + " из " + pagesCount));
            coveringSpan.appendChild(pagesCountSpan);
            coveringSpan.appendChild(document.createTextNode(')'));
            return coveringSpan;
        },
        
    _decorateUserName:
        function(userName, userData) {
            var userScoreClass = 'score-' + this._valueToClassString(userData.gscore);
            var userSpan = document.createElement('span');
            userSpan.className = 'box-field ' + 'userId ' + userScoreClass;
            userSpan.appendChild(document.createTextNode(userName));            
            
            var packedInfo = this.packUserInfo(userData);
            packedInfo.forElm = userSpan;
            packedInfo.className = this.getUserInfoClass(userData, userScoreClass);
            userSpan.onmouseover = createMethodReference_(this, '_showFloatingInfo', packedInfo);             
            userSpan.onmouseout = createMethodReference(this, '_hideFloatingInfo');
            
            return userSpan;
        },
        
    _decorateGroupName:
        function(groupName, groupData) {
            var groupScoreClass = 'score-' + this._groupValueToClassString(groupData.score);
            var groupSpan = document.createElement('span');
            groupSpan.className = 'box-field ' + 'groupId ' + groupScoreClass;
            groupSpan.appendChild(document.createTextNode(groupName));
            
            var packedInfo = this.packGroupInfo(groupData);
            packedInfo.forElm = groupSpan;
            packedInfo.className = this.getGroupInfoClass(groupData, groupScoreClass);
            // packedInfo.className = 'groupInfo ' + groupScoreClass;
            groupSpan.onmouseover = createMethodReference_(this, '_showFloatingInfo', packedInfo);            
            groupSpan.onmouseout = createMethodReference(this, '_hideFloatingInfo');
            
            return groupSpan;        
        
        
            return document.createTextNode(groupName);
        },        
        
    _decorateCard:
        function(cardData) {
            var cardSpan = document.createElement('span');
            var suitClass = this._cardSuitToClassString(cardData.id);
            cardSpan.className = 'box-field ' + 'cardId ' + suitClass;
            cardSpan.innerHTML = cardData.html;
            
            cardSpan.onmouseover = createMethodReference_(this, '_showFloatingInfo', {
                    forElm: cardSpan, 
                    objData: cardData,
                    fieldsData: this.CARD_INFO_FIELDS,
                    className: 'cardInfo ' + suitClass,
                    dataPrefix: 'card-'
                });
            cardSpan.onmouseout = createMethodReference(this, '_hideFloatingInfo');
            
            return cardSpan;
        },
        
    _decorateUScore:
        function(uscore, groupsData) {
            var scoreSpan = document.createElement('span');
            scoreSpan.className = 'info-field userScore score-' + this._valueToClassString(uscore);
            scoreSpan.appendChild(document.createTextNode(uscore));
            
            var secInfo = {
                fgroups: this._makeGroupsList(groupsData)
            }
            
            scoreSpan.onmouseover = createMethodReference_(this, '_showFloatingInfo', {
                    forElm: scoreSpan, 
                    objData: groupsData,
                    fieldsData: this.GCOUNT_INFO_FIELDS,
                    className: 'groupsCount',
                    dataPrefix: 'gcount-',
                    moreFields: secInfo
                });
            scoreSpan.onmouseout = createMethodReference(this, '_hideFloatingInfo');            
            return scoreSpan;
        },
        
    _decorateGScore:
        function(gscore, cardsData) {
            var gscoreSpan = document.createElement('span');
            gscoreSpan.className = 'info-field groupScore score-' + this._groupValueToClassString(gscore);
            gscoreSpan.appendChild(document.createTextNode(gscore));
            
            var secInfo = {
                fcards: this._makeCardsList(cardsData)
            }
            
            gscoreSpan.onmouseover = createMethodReference_(this, '_showFloatingInfo', {
                    forElm: gscoreSpan, 
                    objData: cardsData,
                    fieldsData: this.GSCORE_INFO_FIELDS,
                    className: 'gscoreInfo',
                    dataPrefix: 'gscore-',
                    moreFields: secInfo
                });
            gscoreSpan.onmouseout = createMethodReference(this, '_hideFloatingInfo');
            return gscoreSpan;
        },        
                
    _decorateUsersCount:
        function(ucount, usersData) {
            var ucountSpan = document.createElement('span');
            ucountSpan.className = 'info-field groupUcount';
            ucountSpan.appendChild(document.createTextNode(ucount));
            
            var secInfo = {
                fusers: this._makeUsersList(usersData)
            }
            
            ucountSpan.onmouseover = createMethodReference_(this, '_showFloatingInfo', {
                    forElm: ucountSpan, 
                    objData: usersData,
                    fieldsData: this.UCOUNT_INFO_FIELDS,
                    className: 'usersCountInfo',
                    dataPrefix: 'ucount-',
                    moreFields: secInfo
                });
            ucountSpan.onmouseout = createMethodReference(this, '_hideFloatingInfo');
            return ucountSpan;
        },
        
    _decorateProtectionValue:
        function(prvalue) {
            if (prvalue) {
                var imgElm = document.createElement('img');
                imgElm.src = this.LOCK_IMG_SRC;
                imgElm.alt = 'Password protected';
                imgElm.title = 'Password protected';
                return imgElm;
            } else return document.createTextNode('-');
        },    
        
    _decorateGJoinButton:
        function(groupName, groupData, isAllowed) {
            var joinImg = document.createElement('img');
            joinImg.src = isAllowed ? this.JOIN_IMG_SRC :this.NOACT_IMG_SRC;
            joinImg.alt = isAllowed ? 'Вступить' : 'Нельзя вступить';
            joinImg.title = isAllowed ? 'Вступить' : 'Нельзя вступить';
            joinImg.className = 'jabutton' + (isAllowed ? '' : ' not-allowed');
            if (this._groupJoinClickHandler && isAllowed) joinImg.onclick = createMethodReference_(this, '_groupJoinClickHandler', groupData);
            return joinImg;
        },
        
    _decorateGLeaveButton:
        function(groupName, groupData, isAllowed) {
            var leaveImg = document.createElement('img');
            leaveImg.src = isAllowed ? this.LEAVE_IMG_SRC :this.NOACT_IMG_SRC;
            leaveImg.alt = isAllowed ? 'Покинуть' : 'Нельзя покинуть';
            leaveImg.title = isAllowed ? 'Покинуть' : 'Нельзя покинуть';
            leaveImg.className = 'jabutton' + (isAllowed ? '' : ' not-allowed');
            if (this._groupLeaveClickHandler && isAllowed) leaveImg.onclick = createMethodReference_(this, '_groupLeaveClickHandler', groupData);
            return leaveImg;
        },    
        
    _decorateGroupsCount:
        function(gcount, groupsData) {
            var gcountSpan = document.createElement('span');
            gcountSpan.className = 'info-field userGcount';
            gcountSpan.appendChild(document.createTextNode(gcount));
            
            var secInfo = {
                fgroups: this._makeGroupsList(groupsData)
            }
            
            gcountSpan.onmouseover = createMethodReference_(this, '_showFloatingInfo', {
                    forElm: gcountSpan, 
                    objData: groupsData,
                    fieldsData: this.GCOUNT_INFO_FIELDS,
                    className: 'groupsCount',
                    dataPrefix: 'gcount-',
                    moreFields: secInfo
                });
            gcountSpan.onmouseout = createMethodReference(this, '_hideFloatingInfo');
            return gcountSpan;
        },
        
    _makePageButton:
        function(isPointsForward, tableStatus, totalItemsNum) {
            var imgElm = document.createElement('img');
            var isEnabled = isPointsForward ? ((tableStatus.offset + tableStatus.rows) <= totalItemsNum) : ((tableStatus.offset - tableStatus.rows) >= 0);
            var buttonClassName = 'page-button-' + (isPointsForward ? 'fwd' : 'back') + (isEnabled ? '' : ' disabled-button');
            imgElm.className = buttonClassName;
            imgElm.alt = isPointsForward ? 'Сюда' : 'Туда';
            imgElm.title = isPointsForward ? 'Сюда' : 'Туда';
            imgElm.src = isPointsForward ? this.FWD_BTN_SRC : this.BACK_BTN_SRC;
            var statsBuilder = this;
            imgElm.onclick = function() {
                    if (isPointsForward) { 
                        if ((tableStatus.offset + tableStatus.rows) <= totalItemsNum) {
                            tableStatus.offset += tableStatus.rows;  
                            statsBuilder._refreshTable(tableStatus);
                        } 
                    } else {
                        if ((tableStatus.offset - tableStatus.rows) >= 0) {
                            tableStatus.offset -= tableStatus.rows;  
                            statsBuilder._refreshTable(tableStatus);
                        }
                    }
                    return false;
                }
            return imgElm;
        },
        
    _createPageNavigator:
        function(tableStatus, totalNum, packSize, curOffset) {
        	var totalPages = div_(totalNum, packSize);
        	var pageNum = div(curOffset, packSize).q;
        	var tableElem = document.createElement('table');
        	tableElem.className = this.PAGE_NAV_CLASS;
        	var tbodyElem = document.createElement('tbody');
        	var trowElem = document.createElement('tr');
        	var statsBuilder = this;
            var pageWidth = this.PAGE_NAV_WIDTH;        	
            var lowPage = div(pageNum, pageWidth).q * this.PAGE_NAV_WIDTH;   
            var highPage = ((lowPage + pageWidth) <= totalPages) ? (lowPage + this.PAGE_NAV_WIDTH) : totalPages;
            var blockPagesBackCell = document.createElement('td');            
        	blockPagesBackCell.appendChild(document.createTextNode('<<'));        	
        	if (lowPage >= pageWidth) {        	   
        	   blockPagesBackCell.onclick = function() {
                    if ((tableStatus.offset - (tableStatus.rows * pageWidth)) >= 0) {
                       tableStatus.offset -= pageWidth * tableStatus.rows;  
                       statsBuilder._refreshTable(tableStatus);
                    } 
                    return false;
        	   }
        	} else blockPagesBackCell.className = 'nav-inactive';
        	trowElem.appendChild(blockPagesBackCell);
        	if (lowPage >= pageWidth) blockPagesBackCell.style.cursor = 'pointer'; 
        	for (var i = lowPage; i < highPage; i++) {
        	   var tcellElem = document.createElement('td');
        	   tcellElem.appendChild(document.createTextNode(i + 1));
        	   var firstElmVal = tableStatus.rows * i + 1;
        	   var supElem = document.createElement('sup');
        	   supElem.appendChild(document.createTextNode(firstElmVal));
        	   tcellElem.appendChild(supElem);
        	   tcellElem.style.cursor = 'pointer';
        	   if (i == pageNum) tcellElem.className = 'nav-selected';
        	   (function(i) {
        	       tcellElem.onclick = function() {
                        if ((tableStatus.rows * i) <= totalNum) {
                           tableStatus.offset = tableStatus.rows * i;  
                           statsBuilder._refreshTable(tableStatus);
                        } 
                        return false;
                    } 
        	   })(i);
        	   trowElem.appendChild(tcellElem);
        	};
        	var blockPagesFwdCell = document.createElement('td');
        	blockPagesFwdCell.appendChild(document.createTextNode('>>'));
            if (highPage < totalPages) {                
               blockPagesFwdCell.onclick = function() {
                    if ((tableStatus.offset + tableStatus.rows) <= totalNum) {
                       tableStatus.offset += pageWidth * tableStatus.rows;  
                       statsBuilder._refreshTable(tableStatus);
                    } 
                    return false;
               }
            } else blockPagesFwdCell.className = 'nav-inactive';    	
        	trowElem.appendChild(blockPagesFwdCell);
        	if (highPage < totalPages) blockPagesFwdCell.style.cursor = 'pointer';
        	tbodyElem.appendChild(trowElem);
        	tableElem.appendChild(tbodyElem);
        	return tableElem;
        },
        
    attachDataFields:
        function(elm, boxData) {
            var fieldsData = boxData.fieldsData;
            for (var fieldName in fieldsData) {
            	if ((boxData.objData[fieldName] != null) || (boxData.moreFields[fieldName] != null)) {
                    var fieldElm = document.createElement('div');
                    fieldElm.className = 'field';
                    var fieldLabel = document.createElement('span');
                    fieldLabel.className = 'label';
                    fieldLabel.appendChild(document.createTextNode(fieldsData[fieldName] + ': '));
                    fieldElm.appendChild(fieldLabel);
                    var fieldValue = document.createElement('span');
                    fieldValue.id = boxData.dataPrefix + fieldName;
                    fieldValue.className = 'value';
                    fieldValue.innerHTML = (boxData.objData[fieldName] != null) ? boxData.objData[fieldName] : boxData.moreFields[fieldName];
                    fieldElm.appendChild(fieldValue);
                    elm.appendChild(fieldElm);
            	}
            }        	
        },
        
    getUserInfoClass:
        function(userData, scoreClass) {
            var userScoreClass = scoreClass || ('score-' + this._valueToClassString(userData.gscore));
            var suitColorClass = 'card-' + this._cardSuitToClassString(userData.card.id);
            return 'userInfo ' + userScoreClass + ' ' + suitColorClass;
        },
        
    getGroupInfoClass:
        function(groupData, scoreClass) {
            var groupScoreClass = scoreClass || 'score-' + this._groupValueToClassString(groupData.score);
            return 'groupInfo ' + groupScoreClass;
        },        
        
    _showFloatingInfo:
        function(boxData) {
            //alert(someData + ' ' + someData && someData.className);
            var flInfoElm = this._floatingInfoElm;
            
            flInfoElm.innerHTML = '';
            flInfoElm.style.display = '';
            if (boxData.className) flInfoElm.className = boxData.className;
            flInfoElm.style.position = 'absolute';  
            var spanPos = findPos(boxData.forElm);         
            flInfoElm.style.top = (spanPos.y + boxData.forElm.offsetHeight) + 'px';
            flInfoElm.style.left = (spanPos.x/* + boxData.forElm.offsetWidth*/) + 'px';
            this.attachDataFields(flInfoElm, boxData);
            
        },
        
    _hideFloatingInfo:
        function() {
            var flInfoElm = this._floatingInfoElm;
            flInfoElm.style.position = '';
            flInfoElm.style.display = 'none';
            flInfoElm.className = '';
            flInfoElm.innerHTML = '';
        },
        
    _valueToClassString:
        function(someVal) {
            return (someVal > 0) ? 'positive' : ((someVal < 0) ? 'negative' : 'none');
        },
        
    _groupValueToClassString:
        function(someVal) {
            return (someVal == 21) ? 'win' : ((someVal > 21) ? 'lose' : 'process');
        },
        
    _cardSuitToClassString:
        function(cardId) {
            var suitChar = cardId[1];
            return (((suitChar == 'h') || (suitChar == 'd')) ? 'red' : 'black') + '-suited';
        },        
        
    _makeGroupsList:
        function(groupsData) {
            var groupsString = '';
            //var groupsLength = groupsData.length;
            for (var groupIdx in groupsData) {                
                var groupData = groupsData[groupIdx];
                if (groupData.name) {
                    groupsString += '<span class="group-item group-' + 
                                    this._groupValueToClassString(groupData.score) + 
                    '"> ';
                    groupsString += groupData.name + ':' + '<span class="gucount">' + groupData.users.length + '</span>';
                    groupsString += '(<span class="group-score">' + groupData.score + '</span>)';
                    groupsString += '</span>';
                } else groupsString += '<span class="group-item">' + groupData + '</span> ';               
            }
            return groupsString;
        },
        
    _makeUsersList:
        function(usersData) {
            var usersString = '';
            for (var userIdx in usersData) {                
                var userData = usersData[userIdx];
                if (userData.uname) {
                    var cardClass = this._cardSuitToClassString(userData.card.id);
                    usersString += '<span class="user-item user-' + 
                                    this._valueToClassString(userData.gscore) + 
                    '">';
                    usersString += userData.uname + 
                                ':' + '<span class="ucard ' + cardClass + '">' + userData.card.html + '</span>' /* +
                                ':' + '<span class="ugcount">' + userData.groups.length + '</span>' */;
                    usersString += '(<span class="user-score">' + userData.gscore + '</span>)';
                    usersString += '</span> ';
                } else usersString += '<span class="user-item">' + userData + '</span> ';               
            }
            return usersString;
        }, 
        
    _makeCardsList:
        function(cardsData) {
            var cardsString = '';
            for (var cardIdx in cardsData) {
                var cardData = cardsData[cardIdx];                
                if (cardData.id) {
                    var cardClass = this._cardSuitToClassString(cardData.id);
                    cardsString += '<span class="card-item ' + cardClass + '">';
                    cardsString += '<span class="card-code">' + cardData.html + '</span>'; 
                    cardsString += '(<span class="card-score">' + cardData.score + '</span>)';
                    cardsString += '</span>'; 
                } else cardsString += '<span class="card-item">' + cardData + '</span>';
            }
            return cardsString;
        },
        
    _makeTimeString: 
        function(timeData) {
            if (!timeData || (timeData == '')) return '-';
            var timeString = '<span class="datetime">';
        
            var date = null;
            if (!timeData.wday) {
                date = new Date();
                date.setFullYear(timeData.year);
                date.setMonth(timeData.month);                        
                date.setDate(timeData.day);
            } 
            // '%day% %month% %year%, %wday% %hour%%min%.%sec%'
            // month: 5, day: 16, year: 2008, hour: 12, minute: 40, second: 32
            timeString += projectionalReplace(this.TIME_FORMAT, {
                   day: timeData.day,
                   month: this.TIME_MONTHS[timeData.month - 1],
                   year: timeData.year,
                   wday: this.TIME_WDAYS[(timeData.wday ? timeData.wday : date.getDay()) - 1],
                   hour: timeData.hour,
                   min: timeData.minute,
                   sec: timeData.second
                });
            
            timeString += '</span>';

            return timeString;
        },
        
    packGroupInfo:
        function(groupData) {
            
            var secInfo = {
                fusers: groupData.users ? this._makeUsersList(groupData.users) : null,
                fctime: this._makeTimeString(groupData.ctime),
                fprotected: (groupData.isprotected ? '<img src="' + this.LOCK_IMG_SRC + '" alt="Protected" title="Protected" />' : '-'),
                fucount: groupData.ucount ? groupData.ucount : groupData.users.length  
            }
            
            return { 
                    objData: groupData,
                    fieldsData: this.GROUP_INFO_FIELDS,
                    dataPrefix: 'group-',
                    moreFields: secInfo                    
                };
        },
        
    packUserInfo:
        function(userData) {
        	
            var secInfo = {
                fgroups: userData.groups ? this._makeGroupsList(userData.groups) : null,
                frtime: this._makeTimeString(userData.rtime),
                fcard: userData.card.html,
                fcardname: userData.card.name
            }
            
            return { 
                    objData: userData,
                    fieldsData: this.USER_INFO_FIELDS,
                    dataPrefix: 'user-',
                    moreFields: secInfo                    
                };
        },        
        
    setUserClickHandler:
        function(handlerFunc) {
            this._userClickHandler = handlerFunc;
        },
        
    setGroupClickHandler:
        function(handlerFunc) {
            this._groupClickHandler = handlerFunc;
        },
        
   setGroupJoinClickHandler:
       function(handlerFunc) {
            this._groupJoinClickHandler = handlerFunc;
        },
        
   setGroupLeaveClickHandler:
        function(handlerFunc) {
            this._groupLeaveClickHandler = handlerFunc;
        },
        
   setLoadingDisplayElm:
        function(loadingElm) {
            this._loadingDisplayElm = loadingElm;
        },
        
   _showLoadingStatus:
        function() {
             if (this._loadingDisplayElm) this._loadingDisplayElm.className = '';
             this._loadingProcessesShowing++;
        },

   _hideLoadingStatus:
        function() {
             if (this._loadingDisplayElm && (this._loadingProcessesShowing == 1)) this._loadingDisplayElm.className = 'hidden';
             if (this._loadingProcessesShowing > 0) this._loadingProcessesShowing--;
        } 

});
