PageSourceSearch

https://tesera.ru/js//newtes.js?v=1&395

js tesera.ru collected 2026-09-24 09:15:20 UTC 153,275 bytes, 4,006 lines download raw bytes

1var bearerToken = null;
2var apiHost = 'https://api.tesera.ru';
3var siteUrl = 'https://tesera.ru';
4const mSiteUrl = 'https://m.tesera.ru'
5var limitMarketOnPage = 30;
6var limitMarketOnSearch = 10;
7var getHashCheck = false;
8var is_safari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
9
10$(document).ready(function() {
11    safeClearStorage()
12});
13
14function safeClearStorage()
15{
16    let save = {};
17    $.each(['hash', 'market', 'list-elements', 'games'], function(index, key) {
18        save[key] = localStorage.getItem(key);
19    });
20
21    localStorage.clear();
22
23    $.each(save, function(index, value) {
24        localStorage.setItem(index, value);
25    });
26}
27
28function getHash(hash, login, callback, ip)
29{
30    bearerToken = localStorage.getItem('hash');
31
32    if (getHashCheck === true) {
33        callback();
34        return true;
35    } else {
36        getHashCheck = true;
37    }
38
39    if (login == '') {
40        return false;
41    }
42
43    $.ajax({
44        type: "get",
45        dataType: 'json',
46        url: apiHost + '/auth/info',
47        beforeSend: function (xhr) {
48            xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
49        },
50        success: function (data) {
51            if (data.authenticated !== true) {
52                wakeUp(hash, login, ip,function() {
53                    callback();
54                });
55            } else {
56                $.cookie("auth._token.local", 'Bearer ' + bearerToken, {expires: 0, path: '/', domain: '.tesera.ru', secure: false});
57                userInfo = data;
58                callback();
59            }
60        },
61        error: function (jqXHR, textStatus, errorThrown) {
62            bearerToken = null
63            $.cookie("auth._token.local", null, {expires: -1, domain: '.tesera.ru', path: '/'});
64            localStorage.removeItem('hash')
65
66            console.log('GetHash:', jqXHR, textStatus, errorThrown);
67        },
68        complete: function (jqXHR, textStatus) {
69            if (jqXHR.status === 204) {
70                wakeUp(hash, login, ip,function() {
71                    callback();
72                });
73            }
74        }
75    });
76}
77
78function wakeUp(hash, login, ip, callback)
79{
80    $.ajax({
81        type: "post",
82        dataType: 'json',
83        url: apiHost + '/auth/wakeup',
84        contentType: "application/json",
85        data: JSON.stringify({
86            hash: hash,
87            login: login,
88            ipaddress: ip
89        }),
90        success: function (data) {
91            if (data.token) {
92                localStorage.setItem('hash', data.token)
93                bearerToken = data.token
94
95                $.cookie("auth._token.local", 'Bearer ' + bearerToken, {expires: 10, path: '/', domain: '.tesera.ru', secure: false});
96            } else {
97                bearerToken = null;
98            }
99        },
100        error: function (jqXHR, textStatus, errorThrown) {
101            bearerToken = null
102            $.cookie("auth._token.local", null, {expires: -1, domain: '.tesera.ru', path: '/'});
103            localStorage.removeItem('hash')
104
105            console.log('WakeUp:', jqXHR, textStatus, errorThrown);
106        },
107        complete: function () {
108            callback();
109        }
110    });
111}
112
113function myDate(dateStr, isTime, yearType)
114{
115    if (isTime === undefined) {
116        isTime = true;
117    }
118
119    if (yearType === undefined) {
120        yearType = 1;
121    }
122
123    if (is_safari) {
124        var x = dateStr.substring(0, 10).split('-');
125        var y = dateStr.substring(11, 16).split(':');
126        dateStr = x[1] + "/" + x[2] + "/" + x[0] + " " + y[0] + ":" + y[1] + ":00"
127    }
128
129    var date = new Date(dateStr);
130    date.setTime( date.getTime() - date.getTimezoneOffset()*60*1000 );
131
132    var day = date.getDate();
133    var month = date.getMonth();
134    var year = date.getFullYear();
135
136    var hour = date.getHours();
137    var min = date.getMinutes();
138    var sec = date.getSeconds();
139
140    var monthsStr = ['января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря'];
141
142    if (min.toString().length === 1)
143        min = '0' + min;
144
145    var dateNow = new Date();
146    var yearNow = dateNow.getFullYear();
147    var addYear = '';
148    if (yearNow !== year) {
149        addYear += ' ' + year + '';
150    }
151
152    var result = day + ' ' + monthsStr[month];
153
154    if (yearType === 1) {
155        result += addYear;
156    } else if (yearType === 2 && year !== new Date().getFullYear()) {
157        result += addYear;
158    }
159
160    if (isTime) {
161        result += ', ' + hour + ':' + min;
162    }
163
164    if (is_safari && isNaN(day)) {
165        return dateStr;
166    }
167
168    return result;
169}
170
171function timeSince(dateStr)
172{
173    if (is_safari) {
174        var x = dateStr.substring(0, 10).split('-');
175        var y = dateStr.substring(11, 16).split(':');
176        dateStr = x[1] + "/" + x[2] + "/" + x[0] + " " + y[0] + ":" + y[1] + ":00"
177    }
178
179    let kf = dateStr.length <= 19 ? 18000 : 7200;
180    let date = new Date(dateStr);
181    let seconds = Math.floor((new Date() - date) / 1000 - kf);
182    let interval = Math.floor(seconds / 31536000);
183
184    if (seconds < 0) {
185        return '<span class="online">онлайн</span>';
186    }
187
188    interval = Math.floor(seconds / 2592000);
189    if (interval > 1) {
190        return myDate(dateStr, true, 2);
191    }
192
193    let html = '';
194    let days = Math.floor(seconds / 86400);
195    if (days >= 1) {
196        html += days + " " + drawTimeString(days, 'день' , 'дня' , 'дней');
197    }
198
199    let hours = Math.floor((seconds - days*86400) / 3600);
200    if (hours >= 1) {
201        if (Math.floor(seconds / 86400) >= 1) {
202            html += ' и ';
203        }
204
205        html += hours + " " + drawTimeString(hours, 'час' , 'часа' , 'часов');
206    }
207
208    if (hours > 0 || days > 0) {
209        return html + ' назад';
210    }
211
212    let minute = Math.floor(seconds / 60);
213    if (minute <= 5) {
214        return '<span class="online">онлайн</span>';
215    } else {
216        return minute + " " + drawTimeString(minute, 'минуту' , 'минуты' , 'минут') + ' назад';
217    }
218}
219function drawTimeString(value, first, second, third)
220{
221    return (value === 1 || (value%10 === 1 && value > 20) ? first : (value <= 4 || (value%10 <= 4 && value > 20 && value%10 > 0) ? second : third));
222}
223
224function number_format( number, decimals, dec_point, thousands_sep )
225{
226    var i, j, kw, kd, km;
227
228    if( isNaN(decimals = Math.abs(decimals)) ){
229        decimals = 2;
230    }
231    if( dec_point == undefined ){
232        dec_point = ",";
233    }
234    if( thousands_sep == undefined ){
235        thousands_sep = ".";
236    }
237
238    i = parseInt(number = (+number || 0).toFixed(decimals)) + "";
239
240    if( (j = i.length) > 3 ){
241        j = j % 3;
242    } else{
243        j = 0;
244    }
245
246    km = (j ? i.substr(0, j) + thousands_sep : "");
247    kw = i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + thousands_sep);
248    kd = (decimals ? dec_point + Math.abs(number - i).toFixed(decimals).replace(/-/, 0).slice(2) : "");
249
250    return km + kw + kd;
251}
252
253function unsubscribe(obj)
254{
255    $.ajax({
256        url: apiHost + '/feed/unsubscribe/' + $(obj).attr('data-type') + '/' + $(obj).attr('data-alias'),
257        type: 'DELETE',
258        beforeSend: function (xhr) {
259            xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
260        },
261        success: function(result) {
262            $('div[id="' + $(obj).attr('data-alias') + '"]').each(function () {
263                $(this).remove()
264            })
265        }
266    });
267}
268
269function loadFeed(offset, callback)
270{
271    var feed = '';
272    var next = '';
273    var total = 0;
274
275    $.ajax({
276        type: "get",
277        dataType: 'json',
278        url: apiHost + '/feed/drawevents?limit=30&offset=' + offset,
279        beforeSend: function (xhr) {
280            xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
281        },
282        success: function (data) {
283            $.each(data.items, function(index, item) {
284                var html = '<p class="feed_date">' + myDate(item.createDateTime) + '</p>';
285                html += '<table align="left" border="0" cellpadding="0" cellspacing="0" width="100%"><tr>';
286
287                if (item.firstImg != undefined && item.firstImg != '') {
288                    html += '<td width="70px" valign="top">' +
289                        '<div class="feed_firstImg" style="background: url(' + item.firstImg + ') 0% 0% / 70px 70px no-repeat scroll content-box border-box transparent;"></div>' +
290                        '</td>';
291                } else {
292                    html += '<td width="70px">&nbsp;</td>';
293                }
294
295                html += '<td valign="top">' +
296                    '<div class="feed_message">' + item.title + '</div>' +
297                    '</td>';
298
299                if (item.secondImg != undefined && item.secondImg != '') {
300                    html += '<td width="70px" valign="top">' +
301                        '<div class="feed_secondImg" style="background: url(' + item.secondImg + ') 0% 0% / 70px 70px no-repeat scroll content-box border-box transparent;"></div>' +
302                        '</td>';
303                } else {
304                    html += '<td width="70px">&nbsp;</td>';
305                }
306
307                html += '</tr></table>';
308                html += '<p class="feed_subs"><a href="#" data-type="' + item.subscribeType + '" data-alias="' + item.alias + '" onclick="unsubscribe(this); return false;">Отписаться</a></p>';
309
310                feed += '<div class="feed_item" id="' + item.alias + '">' + html + '</div>';
311            });
312
313            total = data.total;
314            if (data.hasNextPage === true) {
315                next = '<a onclick="loadFeed(' + data.nextPage + ', function(data) { feedDraw(data); }); return false;">Загрузить ещё</a>';
316            }
317
318            if (data.items.length == 0 && data.pages == 0) {
319                feed = '<b>Пока ваша лента пуста</b>' +
320                    '<br /><br />Вы можете следить за:' +
321                    '<br />- комментариями к публикациям;' +
322                    '<br />- обновлениям по играм;' +
323                    '<br />- активностью пользователей.' +
324                    '<br /><br />Подписывайтесь в соответствующем разделе.';
325            }
326
327            callback({
328                'feed': feed,
329                'next': next,
330                'total': total
331            });
332        }
333    });
334}
335
336function feedDraw(data)
337{
338    $('#feed #loader').remove();
339
340    $('#feed').append(data.feed);
341    // $('#feed-count').html(data.total);
342    $('#feed-pages').html(data.next);
343}
344
345function checkMailSubscribe(type, callback)
346{
347    $.ajax({
348        type: "get",
349        dataType: 'json',
350        url: apiHost + '/mail/subscribe/' + type,
351        beforeSend: function (xhr) {
352            xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
353        },
354        success: function (data) {
355            callback(data)
356        }
357    });
358}
359
360function subscribeObject(type, obj)
361{
362    if ($(obj).attr('data-type') == 1) {
363        $.ajax({
364            type: "post",
365            dataType: 'json',
366            contentType: "application/json",
367            data: JSON.stringify({
368                objectType: type
369            }),
370            url: apiHost + '/mail/subscribe',
371            beforeSend: function (xhr) {
372                xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
373            },
374            complete: function () {
375                $(obj).attr('data-type', 2);
376                $(obj).html('Отписаться от Email рассылки');
377            }
378        });
379    } else {
380        $.ajax({
381            type: 'DELETE',
382            dataType: 'json',
383            url: apiHost + '/mail/unsubscribe/' + type,
384            beforeSend: function (xhr) {
385                xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
386            },
387            complete: function () {
388                $(obj).attr('data-type', 1);
389                $(obj).html('Подписаться на Email рассылку');
390            }
391        });
392    }
393}
394
395function checkFeedSubscribe(type, alias, callback)
396{
397    $.ajax({
398        type: "get",
399        dataType: 'json',
400        url: apiHost + '/feed/subscribe/' + type + '/' + alias,
401        beforeSend: function (xhr) {
402            xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
403        },
404        success: function (data) {
405            callback(data)
406        }
407    });
408}
409
410function subscribeObjectFeed(type, alias, obj)
411{
412    if ($(obj).attr('data-type') == 1) {
413        $.ajax({
414            type: "post",
415            dataType: 'json',
416            contentType: "application/json",
417            data: JSON.stringify({
418                objectType: type,
419                alias: alias
420            }),
421            url: apiHost + '/feed/subscribe',
422            beforeSend: function (xhr) {
423                xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
424            },
425            complete: function () {
426                $(obj).attr('data-type', 2);
427                $(obj).html('Отписаться');
428                $(obj).attr('class', 'no_subscribe');
429            }
430        });
431    } else {
432        $.ajax({
433            type: 'DELETE',
434            dataType: 'json',
435            url: apiHost + '/feed/unsubscribe/' + type + '/' + alias,
436            beforeSend: function (xhr) {
437                xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
438            },
439            complete: function () {
440                $(obj).attr('data-type', 1);
441                $(obj).html('Подписаться');
442                $(obj).attr('class', 'is_subscribe');
443            }
444        });
445    }
446}
447
448var filterGame;
449var filterCountry;
450var filterCity;
451var filterCondition;
452var userId;
453var groupStatus;
454var insertNewLoad = true;
455var archiveLineView = false;
456var marketSort;
457
458var advDraw = {
459    'limitRecord': 30,
460    'clearPrev': false,
461    'pagesView': true,
462    'archiveLine': true,
463    'type': undefined,
464    'myPage': false
465};
466var marketView = localStorage.getItem('market');
467
468function marketInit(alias, none)
469{
470    if (marketView === undefined || marketView === null) {
471        marketView = 'list';
472    }
473
474    if (marketView === 'list') {
475        $('#market-list').addClass('active');
476    } else {
477        $('#market-grid').addClass('active');
478    }
479
480    $('#market-list').click(function () {
481        $('#market-list').addClass('active');
482        $('#market-grid').removeClass('active');
483
484        localStorage.setItem('market', 'list');
485        marketView = 'list';
486        resetPart(alias);
487    });
488
489    $('#market-grid').click(function () {
490        $('#market-grid').addClass('active');
491        $('#market-list').removeClass('active');
492
493        localStorage.setItem('market', 'grid');
494        marketView = 'grid';
495        resetPart(alias);
496    });
497}
498
499function marketLoad(limit, offset, alias, callback)
500{
501    var html = '';
502    var next = '';
503    var url = apiHost + '/trade/' + alias + '?limit=' + limit + '&offset=' + offset;
504
505    if (filterGame !== undefined) {
506        url += '&game=' + filterGame;
507    }
508
509    if (filterCountry !== undefined) {
510        url += '&country=' + filterCountry;
511    }
512
513    if (filterCity !== undefined) {
514        url += '&city=' + filterCity;
515    }
516
517    if (filterCondition !== undefined) {
518        url += '&condition=' + filterCondition;
519    }
520
521    if (groupStatus !== undefined) {
522        url += '&groupstatus=' + groupStatus;
523    }
524
525    if (userId !== undefined) {
526        url += '&user=' + userId;
527    } else if (advDraw.type === 'user') {
528
529        if (alias === 'sales' && ownDraw.myPage) {
530            html = '<p class="market_empty">Здесь будут отображаться ваши объявления о продаже той или иной игры. Объявления старше 30 дней уходят в архив. Просроченные объявления можно снова сделать активными, отредактировав их. Внимание! Чтобы новое объявление стало активным (и не ушло сразу в архив), нужно обязательно указать цену и/или состояние.</p>';
531        } else if (alias === 'purchases' && ownDraw.myPage) {
532            html = '<p class="market_empty">Здесь будут отображаться ваши объявления о покупке той или иной игры. Объявления старше 30 дней уходят в архив. Просроченные объявления можно снова сделать активными, отредактировав их. Внимание! Чтобы новое объявление стало активным (и не ушло в архив), нужно обязательно указать цену и/или состояние.</p>';
533        } else {
534            html = '<p class="market_empty">Записей не найдено</p>';
535        }
536
537        callback({
538            'html': html,
539            'total': 0,
540            'totalOther': 0,
541            'next': ''
542        });
543
544        return;
545    }
546
547    if (marketSort !== undefined) {
548        url += '&sort=' + marketSort;
549    }
550
551    $.ajax({
552        type: "get",
553        dataType: 'json',
554        url: url,
555        beforeSend: function (xhr) {
556            xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
557        },
558        success: function (data, textStatus, request) {
559            $.each(data, function(index, item) {
560                if (item.status == 'Archieved' && archiveLineView === false && advDraw.archiveLine === true) {
561                    archiveLineView = true;
562                    html += '<div class="market_hr">АРХИВ</div>';
563                    if (marketView === 'grid') {
564                        html += '<br />';
565                    }
566                }
567
568                html += marketItemDraw(item, alias);
569            });
570        },
571        complete: function (jqXHR) {
572            var offset = jqXHR.getResponseHeader('x-offset');
573            var totalPages = jqXHR.getResponseHeader('x-total-pages');
574            var totalOther = jqXHR.getResponseHeader('x-total-other');
575
576            if (parseInt(offset) + 1 < totalPages) {
577                next += '<a onclick="pageLoad(' + limit + ', ' + (parseInt(offset) + 1) + ', \'' + alias + '\'); return false;">Загрузить ещё</a>';
578            }
579
580            if (html === '') {
581                if (alias === 'sales' && ownDraw.myPage) {
582                    html = '<p class="market_empty">Здесь будут отображаться ваши объявления о продаже той или иной игры. Объявления старше 30 дней уходят в архив. Просроченные объявления можно снова сделать активными, отредактировав их. Внимание! Чтобы новое объявление стало активным (и не ушло сразу в архив), нужно обязательно указать цену и/или состояние.</p>';
583                } else if (alias === 'purchases' && ownDraw.myPage) {
584                    html = '<p class="market_empty">Здесь будут отображаться ваши объявления о покупке той или иной игры. Объявления старше 30 дней уходят в архив. Просроченные объявления можно снова сделать активными, отредактировав их. Внимание! Чтобы новое объявление стало активным (и не ушло в архив), нужно обязательно указать цену и/или состояние.</p>';
585                } else {
586                    html = '<p class="market_empty">Записей не найдено</p>';
587                }
588            }
589
590            callback({
591                'html': html,
592                'total': jqXHR.getResponseHeader('x-total-count'),
593                'totalOther': totalOther,
594                'next': next
595            });
596        }
597    });
598}
599
600function pageLoad(limit, offset, alias)
601{
602    marketLoad(limit, offset, alias, function(data) {
603        marketDraw(data, alias)
604    });
605}
606
607function marketItemDraw(item, alias)
608{
609    localStorage.setItem(alias + '-' + item.id, JSON.stringify({
610        'id': item.game.teseraId,
611        'title': item.game.title,
612        'price': item.price,
613        'condition': item.condition,
614        'comment': item.content,
615        'status': item.status,
616        'author': {'name': item.author.name, 'login': item.author.login, 'id': item.author.teseraId},
617        'city': item.city,
618        'country': item.country
619    }));
620
621    if (marketView === 'list' || marketView === undefined || marketView === null || marketView === 'null') {
622        return marketItemDrawList(item, alias);
623    } else if (marketView === 'grid') {
624        return marketItemDrawGrid(item, alias);
625    }
626}
627
628function marketItemDrawList(item, alias)
629{
630    var html = '<div class="feed_item" id="adv-' + item.id + '">';
631
632    html += '<p class="feed_date">' + myDate(item.publicationDateUtc) + '</p>';
633
634    if (advDraw.type != 'user') {
635        html += '<p class="feed_author_name"><a href="/user/' + item.author.teseraId + '">' + item.author.name + '';
636        if (item.author.name.toString().toLowerCase() !== item.author.login.toString().toLowerCase()) {
637            html += ' (' + item.author.login + ')';
638        }
639        html += '</a></p>';
640    }
641
642    html += '<table align="left" border="0" cellpadding="0" cellspacing="0" width="100%"><tr>';
643
644    var empty = '';
645    var status = '';
646    if (item.status == 'Archieved') {
647        status = '<span class="ad_status2">В архиве</span>';
648        empty =  '<div class="not_active"></div>';
649    }
650
651    if (item.game != undefined && item.game.photoUrl != undefined && item.game.photoUrl != '') {
652        html += '<td width="70px" valign="top">' +
653            '<div class="feed_firstImg" style="background: url(' + item.game.photoUrl + ') 0% 0% / 70px 70px no-repeat scroll content-box border-box transparent;">' + status + '</div>' +
654            '</td>';
655    } else {
656        html += '<td width="70px">&nbsp;</td>';
657    }
658
659    html += '<td valign="top">' +
660        '<div class="feed_message">' + empty + '<b><a href="/game/' + item.game.teseraId + '">' + item.game.title + '</a></b>';
661
662    if (item.condition === 'Used') {
663        html += '<span id="condition-' + alias + '-' + item.id + '">, б/у</span>';
664    } else if (item.condition === 'New') {
665        html += '<span id="condition-' + alias + '-' + item.id + '">, новая</span>';
666    } else if (item.condition === 'Any' && alias === 'purchases') {
667        html += '<span id="condition-' + alias + '-' + item.id + '">, состояние любое</span>';
668    } else {
669        html += '<span id="condition-' + alias + '-' + item.id + '"></span>'
670    }
671
672    if (item.price > 0) {
673        html += '<span id="price-' + alias + '-' + item.id + '">, <b>' + item.price + ' ' + item.currency + '</b></span>';
674    } else {
675        html += '<span id="price-' + alias + '-' + item.id + '"></span>';
676    }
677
678    if (item.country !== undefined && item.city !== undefined) {
679        html += '<br />' + item.country.name + ', <a href="/location/' + item.country.teseraId + '/' + item.city.teseraId + '/' + (alias === 'sales' ? 'sells' : 'buys') + '">' + item.city.name + '</a>';
680    } else if (item.city !== undefined && filterCountry !== undefined) {
681        html += '<br /><a href="/location/' + filterCountry + '/' + item.city.teseraId + '/' + (alias === 'sales' ? 'sells' : 'buys') + '">' + item.city.name + '</a>';
682    }
683
684    if (item.content !== undefined) {
685        html += '<span id="comment-' + alias + '-' + item.id + '"><p class="feed_comment"><i>«' + item.content + '»</i></p></span>';
686    } else {
687        html += '<span id="comment-' + alias + '-' + item.id + '"></span>';
688    }
689
690    html += '</div>' +
691        '</td>';
692
693    if (item.author != undefined && item.author.avatarUrl != undefined && item.author.avatarUrl != '' && advDraw.type != 'user') {
694        html += '<td width="70px" valign="top">' +
695            '<div class="feed_secondImg" style="background: url(' + item.author.avatarUrl + ') 0% 0% / 70px 70px no-repeat scroll content-box border-box transparent;"></div>' +
696            '</td>';
697    } else if (advDraw.myPage === true) {
698        html += '<td width="110px" class="market_menu">';
699
700        if (item.status == 'Archieved' && (item.condition !== 'Unknown' || item.price > 0)) {
701            html += '<a class="market_activate" href="#" onclick="activateAdv(' + item.id + ', \'' + alias + '\'); return false;"><img src="https://tesera.ru/i/green-reg.gif"/>обновить</a>';
702        }
703
704        html += '<a class="market_edit" href="#" onclick="editAdv(' + item.id + ', \'' + alias + '\'); return false;"><img src="https://tesera.ru/i/x-edit.gif"/>редактировать</a>';
705        html += '<a class="market_del" href="#" onclick="delAdv(' + item.id + ', \'' + alias + '\'); return false;"><img src="https://tesera.ru/i/x-delete.gif"/>удалить</a>';
706        html += '</td>';
707
708
709    } else {
710        html += '<td width="70px">&nbsp;</td>';
711    }
712
713    html += '</tr></table></div>';
714
715    return html;
716}
717
718function marketItemDrawGrid(item, alias)
719{
720    gamePosition += 1;
721    var html = '<div class="c_game_item ' + (advDraw.pagesView === false ? 'mb0' : '') + '">';
722
723    if (advDraw.myPage) {
724        html += '<img src="/i/x-edit.gif" onclick="editAdv(' + item.id + ', \'' + alias + '\'); return false;" class="c_game_upd"/>';
725        html += '<img src="/i/x-delete.gif" onclick="delAdv(' + item.id + ', \'' + alias + '\'); return false;" class="c_game_del"/>';
726
727        if (item.status == 'Archieved' && (item.condition !== 'Unknown' || item.price > 0)) {
728            html += '<img src="/i/green-reg.gif" onclick="activateAdv(' + item.id + ', \'' + alias + '\'); return false;" class="c_game_act"/>';
729        }
730    }
731
732    var logo = item.game.photoUrl === undefined ? 'https://tesera.ru/images/items/1175875,3/124x124xpa/photo1.' : item.game.photoUrl;
733    html += '<a class="c_game_img_wrap" href="/game/' + item.game.alias + '"><img class="c_game_img2" src="' + logo + '"/></a>';
734
735    var comment = '';
736    if (item.content !== undefined) {
737        comment = '<span class="c_game_comment_icon" onclick="marketInfo(' + item.id + ', \'' + alias + '\'); return false;" >1</span>';
738    }
739
740    var title = item.game.title.length > 34 ? item.game.title.substring(0, 30) + '&hellip;' : item.game.title;
741    html += '<p class="m_game_title"><a title="' + item.game.title + '" href="/game/' + item.game.alias + '">' + title + '</a> ' + comment + ' </p>';
742
743    if (item.status === 'Archieved') {
744        html += '<div class="m_game_archive">В архиве</div>';
745    }
746
747    if (item.price > 0) {
748        html += '<div class="m_game_price"><span id="price-g-' + alias + '-' + item.id + '">' + item.price + ' ' + item.currency + '</span></div>';
749    }
750
751    html += '</div>';
752
753    return html;
754}
755
756function marketInfo(advId, alias)
757{
758    var data = JSON.parse(localStorage.getItem(alias + '-' + advId));
759
760    var author = '<a href="/user/' + data.author.id + '">' + data.author.name;
761    if (data.author.name.toString().toLowerCase() !== data.author.login.toString().toLowerCase()) {
762        author += ' (' + data.author.login + ')';
763    }
764    author += '</a>';
765
766    var location = '';
767    if (data.country !== undefined && data.city !== undefined) {
768        location += ', ' + data.country.name + ', <a href="/location/' + data.country.teseraId + '/' + data.city.teseraId + '/' + (alias === 'sales' ? 'sells' : 'buys') + '">' + data.city.name + '</a>';
769    } else if (data.city !== undefined && filterCountry !== undefined) {
770        location += ', <a href="/location/' + filterCountry + '/' + data.city.teseraId + '/' + (alias === 'sales' ? 'sells' : 'buys') + '">' + data.city.name + '</a>';
771    }
772
773    var html = '<div class="popuper" id="popup-info-game-' + alias + '-' + advId + '" type="game_rel_comments0" style="z-index: 10000; margin-left: -284px; margin-top: -143px; display: block;" id="popuper_' + data.id  + '_rel_link_user"><div class="popup-top">&nbsp;</div>' +
774        '<div class="popup">' +
775        '<h3>' + author + location + '</h3>' +
776        '<h2>Продаёт игру - ' + data.title + '</h2>' +
777        '<img class="close" src="/img/p_close.gif" border="0" onclick="closeInfoPopup(' + advId + ', \'' + alias + '\'); return false;">' +
778        '<br>' +
779        '<form id="rel">' +
780        '<table cellpadding="0" cellspacing="0" border="0" class="form">' +
781        '<tbody><tr>' +
782        '<td>' +
783        '<h3 style="width:100%; margin-left: 10px;"><i>«' + nl2br(data.comment) + '»</i></h3>' +
784        '</td>' +
785        '</tr>' +
786        '<tr><td colspan="2"><hr></td></tr>' +
787        '<tr>' +
788        '<td>';
789
790    var cond = '';
791    if (data.condition === 'Used') {
792        cond = 'б/у';
793    } else if (data.condition === 'New') {
794        cond = 'новая';
795    } else if (data.condition === 'Any') {
796        cond = 'в любом состоянии';
797    }
798
799    html += '<br><h3 style="margin-left: 10px">Игра <b>' + cond + '</b></h3>' +
800        '<br><h3 style="margin-left: 10px">Цена <b>' + data.price + '</b> руб.</h3>' +
801        '</td>' +
802        '</tr>' +
803        '</tbody></table>' +
804        '</form>' +
805        '</div>' +
806        '<div class="popup-bottom">&nbsp;</div>' +
807        '</div>';
808
809    $('#popup-area').html(html);
810}
811
812function closeInfoPopup(advId, alias)
813{
814    $('#popup-info-game-' + alias + '-' + advId).remove();
815}
816
817function marketDraw(data, alias)
818{
819    $('#market-' + alias + ' #loader').remove();
820
821    if (advDraw.clearPrev === true || insertNewLoad === true) {
822        $('#market-' + alias).html(data.html);
823    } else {
824        $('#market-' + alias).append(data.html);
825    }
826
827    insertNewLoad = false;
828
829    if (data.total !== undefined) {
830        if (groupStatus === 'NotDelete' && data.totalOther !== null) {
831            var t1 = data.total - data.totalOther;
832            var t2 = data.totalOther;
833
834            $('#market-' + alias + '-count').html(t1 + ' активных (' + t2 + ' в архиве)');
835        } else {
836            $('#market-' + alias + '-count').html(data.total);
837        }
838    }
839
840    if (advDraw.pagesView === true) {
841        $('#market-pages').html(data.next);
842    }
843}
844
845function marketGameSearch(string, alias)
846{
847    if (string.length <= 1) {
848        return false;
849    }
850
851    $.ajax({
852        type: "get",
853        dataType: 'json',
854        url: apiHost + '/search/games?query=' + string + '&limit=' + limitMarketOnSearch,
855        beforeSend: function (xhr) {
856            xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
857        },
858        success: function (data) {
859            var html = '';
860            $.each(data, function(index, item) {
861                html += '<li onclick="marketFilterGame(\'' + item.alias + '\', \'' + alias + '\', \'' + item.title + '\');">' + item.title + '</li>';
862            });
863
864            if (data.length <= 0) {
865                html = '<span>Результатов не найдено</span>';
866            }
867
868            $('#f-game-result').html(html);
869            $('#f-game-result').css('display', 'block');
870        },
871        complete: function () {
872            $('#f-game-result').focus();
873        }
874    });
875}
876
877function marketCountrySearch(string, alias)
878{
879    if (string.length <= 1) {
880        return false;
881    }
882
883    $.ajax({
884        type: "get",
885        dataType: 'json',
886        url: apiHost + '/search/countries?query=' + string + '&limit=' + limitMarketOnSearch,
887        beforeSend: function (xhr) {
888            xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
889        },
890        success: function (data) {
891            var html = '';
892            $.each(data, function(index, item) {
893                html += '<li onclick="marketFilterCountry(\'' + item.alias + '\', \'' + alias + '\', \'' + item.name + '\');">' + item.name + '</li>';
894            });
895
896            if (data.length <= 0) {
897                html = '<span>Результатов не найдено</span>';
898            }
899
900            $('#f-country-result').html(html);
901            $('#f-country-result').css('display', 'block');
902        },
903        complete: function () {
904            $('#f-country-result').focus();
905        }
906    });
907}
908
909function marketCitySearch(string, alias, type)
910{
911    if (string.length <= 1) {
912        return false;
913    }
914    if (type === undefined) {
915        type = 'market';
916    }
917
918    $.ajax({
919        type: "get",
920        dataType: 'json',
921        url: apiHost + '/search/cities?query=' + string + '&limit=' + limitMarketOnSearch,
922        beforeSend: function (xhr) {
923            xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
924        },
925        success: function (data) {
926            var html = '';
927            $.each(data, function(index, item) {
928                html += '<li onclick="' + type + 'FilterCity(\'' + item.alias + '\', \'' + alias + '\', \'' + item.name + '\');">' + item.name + '</li>';
929            });
930
931            if (data.length <= 0) {
932                html = '<span>Результатов не найдено</span>';
933            }
934
935            $('#f-city-result').html(html);
936            $('#f-city-result').css('display', 'block');
937        },
938        complete: function () {
939            $('#f-city-result').focus();
940        }
941    });
942}
943
944function marketFilterGame(teseraId, alias, title)
945{
946    $('#f-game').val(title);
947    $('#f-game').css('color', '#0e8400');
948
949    insertNewLoad = true;
950    archiveLineView = false;
951
952    filterGame = teseraId;
953    marketLoad(limitMarketOnPage, 0, alias, function(data) {
954        marketDraw(data, alias);
955    });
956}
957
958function marketFilterCountry(teseraId, alias, title)
959{
960    $('#f-country').val(title);
961    $('#f-country').css('color', '#0e8400');
962
963    insertNewLoad = true;
964    archiveLineView = false;
965
966    filterCountry = teseraId;
967    marketLoad(limitMarketOnPage, 0, alias, function(data) {
968        marketDraw(data, alias);
969    });
970}
971
972function marketFilterCity(teseraId, alias, title)
973{
974    $('#f-city').val(title);
975    $('#f-city').css('color', '#0e8400');
976
977    insertNewLoad = true;
978    archiveLineView = false;
979
980    filterCity = teseraId;
981    marketLoad(limitMarketOnPage, 0, alias, function(data) {
982        marketDraw(data, alias);
983    });
984}
985
986function marketFilterCondition(value, alias)
987{
988    $('#f-condition').css('color', '#0e8400');
989
990    insertNewLoad = true;
991    archiveLineView = false;
992
993    filterCondition = value;
994    marketLoad(limitMarketOnPage, 0, alias, function(data) {
995        marketDraw(data, alias);
996    });
997}
998
999function marketFilterSort(value, alias)
1000{
1001    insertNewLoad = true;
1002    archiveLineView = false;
1003
1004    marketSort = value;
1005
1006    marketLoad(advDraw.limitRecord, 0, alias, function(data) {
1007        marketDraw(data, alias);
1008    });
1009}
1010
1011function resetFilters(alias, none)
1012{
1013    if (none != 'game') {
1014        filterGame = undefined;
1015    }
1016
1017    if (none != 'city') {
1018        if (none != 'country') {
1019            filterCountry = undefined;
1020        }
1021
1022        filterCity = undefined;
1023    }
1024
1025    if (none != 'user') {
1026        userId = undefined;
1027    }
1028
1029    filterCondition = undefined;
1030
1031    insertNewLoad = true;
1032    archiveLineView = false;
1033
1034    $('#f-game').val('');
1035    $('#f-game').css('color', '#000');
1036    $('#f-country').val('');
1037    $('#f-country').css('color', '#000');
1038    $('#f-city').val('');
1039    $('#f-city').css('color', '#000');
1040    $('#f-condition').val(0);
1041    $('#f-condition').css('color', '#000');
1042
1043    marketLoad(advDraw.limitRecord, 0, alias, function(data) {
1044        marketDraw(data, alias);
1045    });
1046}
1047
1048function resetPart(alias)
1049{
1050    insertNewLoad = true;
1051    archiveLineView = false;
1052
1053    marketLoad(advDraw.limitRecord, 0, alias, function(data) {
1054        marketDraw(data, alias);
1055    });
1056}
1057
1058/*************************************
1059 * Trade add
1060 * */
1061function addSale(callback)
1062{
1063    var gameAlias = $('#linked_id').val();
1064    var comment = $('#rel_comments').val();
1065    var price = parseFloat($('input[name="rel_price"]').val());
1066    var condition = $('input[name="rel_is_cond"]:checked').val();
1067
1068    $.ajax({
1069        type: "post",
1070        dataType: 'json',
1071        url: apiHost + '/trade/sales',
1072        contentType: "application/json",
1073        data: JSON.stringify({
1074            price: price,
1075            content: comment,
1076            gamealias: gameAlias,
1077            condition: condition
1078        }),
1079        beforeSend: function (xhr) {
1080            xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
1081        },
1082        complete: function () {
1083            callback();
1084        }
1085    });
1086}
1087
1088function delAdv(id, alias, check, isRelation, callback)
1089{
1090    if (check === undefined) {
1091        check = true;
1092    }
1093
1094    if (isRelation === undefined) {
1095        isRelation = false;
1096    }
1097
1098    if (check === true) {
1099        if (!confirm('Вы уверены что хотите удалить это объявление?')) {
1100            return false;
1101        }
1102    }
1103
1104    $.ajax({
1105        type: "delete",
1106        url: apiHost + '/trade/' + alias + '/' + id + (isRelation === true ? '/relation' : ''),
1107        beforeSend: function (xhr) {
1108            xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
1109        },
1110        complete: function (jqXHR) {
1111            insertNewLoad = true;
1112            archiveLineView = false;
1113
1114            if (callback !== undefined) {
1115                callback();
1116            } else {
1117                if (check === true) {
1118                    marketLoad(advDraw.limitRecord, 0, alias, function(data) {
1119                        marketDraw(data, alias);
1120                    });
1121                }
1122            }
1123        }
1124    });
1125}
1126
1127function activateAdv(id, alias)
1128{
1129    $.ajax({
1130        type: "put",
1131        url: apiHost + '/trade/' + alias + '/' + id + '/activate',
1132        beforeSend: function (xhr) {
1133            xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
1134        },
1135        complete: function (jqXHR) {
1136            insertNewLoad = true;
1137            archiveLineView = false;
1138
1139            marketLoad(advDraw.limitRecord, 0, alias, function(data) {
1140                marketDraw(data, alias);
1141            });
1142        }
1143    });
1144}
1145
1146function addPurchase(callback)
1147{
1148    var gameAlias = $('#linked_id').val();
1149    var comment = $('#rel_comments').val();
1150    var price = parseFloat($('input[name="rel_price"]').val());
1151    var condition;
1152    if ($('#rel_is_new').is(':checked') === false && $('#rel_is_old').is(':checked') === true) {
1153        condition = 'used';
1154    } else if ($('#rel_is_new').is(':checked') === true && $('#rel_is_old').is(':checked') === false) {
1155        condition = 'new';
1156    } else if ($('#rel_is_new').is(':checked') === true && $('#rel_is_old').is(':checked') === true) {
1157        condition = 'any';
1158    }
1159
1160    $.ajax({
1161        type: "post",
1162        dataType: 'json',
1163        url: apiHost + '/trade/purchases',
1164        contentType: "application/json",
1165        data: JSON.stringify({
1166            price: price,
1167            content: comment,
1168            gamealias: gameAlias,
1169            condition: condition
1170        }),
1171        beforeSend: function (xhr) {
1172            xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
1173        },
1174        success: function (data, textStatus, request) {
1175            callback(data);
1176        },
1177        complete: function () {
1178            //
1179        }
1180    });
1181}
1182
1183
1184function editAdv(advId, alias)
1185{
1186    openPopup(advId, alias);
1187}
1188
1189function openPopup(advId, alias)
1190{
1191    var data = JSON.parse(localStorage.getItem(alias + '-' + advId));
1192
1193    var html = '<div class="popuper" id="popup-edit-game-' + alias + '-' + advId + '" style="z-index: 10000; margin-left: -284px; margin-top: -267px; display: block;">' +
1194        '<div class="popup-top" style="background-position: 0 1px;">&nbsp;</div>\n' +
1195        '<div class="popup">';
1196
1197    html += '<h5>' + data.title + '</h5>';
1198
1199    if (alias == 'sales') {
1200        html += '<h2>Редактирование объявления продажи</h2>';
1201    } else {
1202        html += '<h2>Редактирование объявления покупки</h2>';
1203    }
1204
1205    html += '<h5 style="color:red">Для размещения объявления на "Барахолке" — укажите состояние игры и/или её цену.</h5>';
1206    html += '<img class="close" src="/img/p_close.gif" border="0" onclick="closeNTPopup(' + advId + ', \'' + alias + '\'); return false;">';
1207    html += '<form>' +
1208        '<input type="hidden" name="id" value="' + advId + '">' +
1209        '<input type="hidden" name="linked_id" value="' + data.id + '">' +
1210        '<table cellpadding="0" cellspacing="0" border="0" class="form">\n' +
1211        '<tbody><tr><td colspan="2"><hr></td></tr>\n' +
1212        '    <tr>\n' +
1213        '      <td width="160" valign="top"><h3>комментарий</h3></td>\n' +
1214        '      <td>\n' +
1215        '        <textarea name="rel_comments" style="width:100%; height: 130px"></textarea>\n' +
1216        '      </td>\n' +
1217        '    </tr>\n' +
1218        '\n' +
1219        '    <tr><td colspan="2">&nbsp;</td></tr>\n' +
1220        '\n' +
1221        '    <tr>';
1222
1223    if (alias === 'sales') {
1224        html += '<td width="160" valign="top"><h3>игра новая?</h3></td>';
1225    } else {
1226        html += '<td width="160" valign="top"></td>'
1227    }
1228
1229    html += '<td>\n' +
1230        '<input style="float: left; margin-right: 10px" type="radio" name="rel_is_cond" value="new" id="rel_is_new">\n' +
1231        '<h3 style="float: left; margin-right: 20px"><label for="rel_is_new">новую?</label></h3>\n' +
1232        '<input style="float: left; margin-right: 10px" type="radio" name="rel_is_cond" value="used" id="rel_is_old">\n' +
1233        '<h3><label for="rel_is_old">б.у.?</label></h3>';
1234
1235    if (alias === 'sales') {
1236        html += '<input style="float: left; margin-right: 10px" type="radio" name="rel_is_cond" value="no" id="rel_is_no" checked="checked">\n' +
1237            '<h3><label for="rel_is_no">не указано</label></h3>';
1238    } else if (alias === 'purchases') {
1239        html += '<input style="float: left; margin-right: 10px" type="radio" name="rel_is_cond" value="any" id="rel_is_any" checked="checked">\n' +
1240            '<h3><label for="rel_is_no">любое</label></h3>';
1241    }
1242
1243    html += '</td>' +
1244        '    </tr>' +
1245        '    <tr><td colspan="2">&nbsp;</td></tr>\n' +
1246        '    <tr>';
1247
1248    if (alias === 'sales') {
1249        html += '<td width="160" valign="top"><h3>цена продажи</h3></td>';
1250    } else if (alias === 'purchases') {
1251        html += '<td width="160" valign="top"><h3>пожелания по цене</h3></td>';
1252    }
1253
1254    html += '<td>\n' +
1255        '        <input type="text" name="rel_price" value="" maxlength="10" style="width: 80px">\n' +
1256        '      </td>\n' +
1257        '    </tr>\n' +
1258        '\n' +
1259        '    \n' +
1260        '    <tr><td colspan="2">\n' +
1261        '      <div id="ajx_loader" class="ajx_loader"></div>\n' +
1262        '    </td></tr>\n' +
1263        '    <tr><td colspan="2">';
1264
1265    if (alias === 'sales') {
1266        html += '<h5 style="color:red">Укажите, по возможности, в каком состоянии, по какой минимальной цене, каким способом вы хотели бы передать игру</h5>';
1267    } else if (alias === 'purchases') {
1268        html += '<h5 style="color:red">Укажите, по возможности, в каком состоянии, по какой минимальной цене, каким способом вы хотели бы получить игру</h5>';
1269    }
1270
1271    html += '</td></tr>\n' +
1272        '    <tr><td colspan="2"><hr></td></tr>\n' +
1273        '    <tr><td colspan="2">\n' +
1274        '      <div form="rel" class="submit save green create" onclick="savePopup(' + advId + ', \'' + alias +'\')"><span>сохранить</span></div>\n' +
1275        '    </td></tr>\n' +
1276        '    </tbody></table>' +
1277        '</form>';
1278
1279    html += '</div><div class="popup-bottom">&nbsp;</div>';
1280
1281    $('#popup-area-' + alias).html(html);
1282
1283    if (data.condition !== undefined) {
1284        $('input[name="rel_is_cond"]').filter('[value="' + data.condition.toLowerCase() + '"]').attr('checked', true);
1285    }
1286    if (data.comment !== undefined) {
1287        $('textarea[name="rel_comments"]').html(data.comment)
1288    }
1289    if (data.price !== undefined) {
1290        $('input[name="rel_price"]').val(data.price);
1291    }
1292}
1293
1294function closeNTPopup(advId, alias)
1295{
1296    $('#popup-edit-game-' + alias + '-' + advId).remove();
1297}
1298
1299function savePopup(advId, alias)
1300{
1301    var condition = $('input[name="rel_is_cond"]:checked').val();
1302    var gameAlias = $('#linked_id').val();
1303    var comment = $('textarea[name="rel_comments"]').val();
1304    var price = parseFloat($('input[name="rel_price"]').val());
1305
1306    var data = JSON.parse(localStorage.getItem(alias + '-' + advId));
1307
1308    $.ajax({
1309        type: "put",
1310        dataType: 'json',
1311        url: apiHost + '/trade/' + alias + '/' + advId,
1312        contentType: "application/json",
1313        data: JSON.stringify({
1314            price: price,
1315            content: comment,
1316            condition: condition
1317        }),
1318        beforeSend: function (xhr) {
1319            xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
1320        },
1321        success: function (data2) {
1322            if (data2.status === 'Active' && data.status !== data2.status) {
1323                resetFilters(alias, 'user');
1324            }
1325
1326            localStorage.setItem(alias + '-' + advId, JSON.stringify({
1327                'id': gameAlias,
1328                'title': data.title,
1329                'price': price,
1330                'condition': condition,
1331                'comment': comment,
1332                'status': data2.status,
1333                'author': {'name': data.author.name, 'login': data.author.login, 'id': data.author.id},
1334                'city': data.city,
1335                'country': data.country
1336            }));
1337        },
1338        complete: function () {
1339            if (price > 0) {
1340                $('#price-' + alias + '-' + advId).html(', <b>' + price + ' RUB</b>');
1341                $('#price-g-' + alias + '-' + advId).html(price + ' RUB');
1342            } else {
1343                $('#price-' + alias + '-' + advId).html('');
1344            }
1345
1346            if (condition !== undefined && condition !== 'no') {
1347                var value = condition === 'new' ? 'новая' : (condition === 'used' ? 'б/у' : (condition === 'any' ? 'состояние любое' : ''));
1348                $('#condition-' + alias + '-' + advId).html(', ' + value);
1349            } else {
1350                $('#condition-' + alias + '-' + advId).html('');
1351            }
1352
1353            if (comment !== undefined && comment.length > 0) {
1354                $('#comment-' + alias + '-' + advId).html('<p class="feed_comment"><i>«' + comment + '»</i></p>');
1355            } else {
1356                $('#comment-' + alias + '-' + advId).html('');
1357            }
1358
1359            closeNTPopup(advId, alias);
1360        }
1361    });
1362}
1363
1364/*************************************
1365 * Diary
1366 * */
1367function diaryLoad(limit, offset, callback)
1368{
1369    if (limit === undefined) {
1370        limit = 30;
1371    }
1372    if (offset === undefined) {
1373        offset = 0;
1374    }
1375
1376    var html = '';
1377    var next = '';
1378    var url = apiHost + '/publications?offset=' + offset + '&limit=' + limit + '&diary=true';
1379
1380    $.ajax({
1381        type: "get",
1382        dataType: 'json',
1383        url: url,
1384        beforeSend: function (xhr) {
1385            xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
1386        },
1387        success: function (data, textStatus, request) {
1388            $.each(data, function(index, item) {
1389                html += diaryItemDraw(item);
1390            });
1391        },
1392        complete: function (jqXHR) {
1393            var offset = jqXHR.getResponseHeader('x-offset');
1394            var totalPages = jqXHR.getResponseHeader('x-total-pages');
1395
1396            if (parseInt(offset) + 1 < totalPages) {
1397                next += '<a onclick="pageDiaryLoad(' + limit + ', ' + (parseInt(offset) + 1) + '); return false;">Загрузить ещё</a>';
1398            }
1399
1400            if (html === '') {
1401                html = '<p class="market_empty">Дневники не найдены</p>';
1402            }
1403
1404            callback({
1405                'html': html,
1406                'total': jqXHR.getResponseHeader('x-total-count'),
1407                'next': next
1408            });
1409        }
1410    });
1411}
1412
1413function pageDiaryLoad(limit, offset)
1414{
1415    diaryLoad(limit, offset, function(data) {
1416        diaryDraw(data)
1417    });
1418}
1419
1420function diaryItemDraw(item)
1421{
1422    var html = '<div class="feed_item">';
1423
1424    var typeWord = item.objectType.toString().toLowerCase() === 'journal' ? 'отчёт' : 'мысль';
1425    html += '<p class="diary_author_name"><a href="/user/' + item.author.teseraId + '">' + item.author.name + '';
1426    if (item.author.name.toString().toLowerCase() !== item.author.login.toString().toLowerCase()) {
1427        html += ' (' + item.author.login + ')';
1428    }
1429    html += '</a></p>';
1430    html += '<p class="diary_date">' + myDate(item.creationDateUtc) + '</p>';
1431
1432    var addRating = '';
1433    if (item.rating > 0) {
1434        addRating += '<div class="diary_rating"><img src="/i/icn1.gif" width="24" height="29" alt="Оценка пользователей" title="Оценка пользователей"><span>' + item.rating + '</span><sub>' + item.numVotes + '</sub></div>';
1435    }
1436
1437    html += '<h2 class="diary_title"><a href="/user/' + item.author.teseraId + '/' + item.objectType.toString().toLowerCase() + '/' + item.objectId + '">' + item.title + '</a><span>' + typeWord + '</span>' + addRating + '</h2>';
1438
1439    var ava = item.author.avatarUrl === undefined ? 'https://tesera.ru/images/items/1366656,22/125x125/photo.' : item.author.avatarUrl;
1440    html += '<table align="left" border="0" cellpadding="0" cellspacing="0" width="100%">' +
1441        '<tr>' +
1442        '<td width="70px" valign="top"><div class="feed_firstImg" style="background: url(' + ava + ') 0% 0% / 70px 70px no-repeat scroll content-box border-box transparent;"></div></td>' +
1443        '<td valign="top"><div class="feed_message">' + item.contentShort + '</div></td>' +
1444        '</tr>' +
1445        '<tr>' +
1446        '<td class="diary_second_line">';
1447
1448    if (item.objectType.toString().toLowerCase() === 'journal') {
1449        html += '<div class="diary_user_rating"><img src="/i/icn3.gif" width="24" height="29" title="Рейтинг"><span>' + item.author.rating + '</span></div>';
1450    }
1451
1452    html += '</td><td class="diary_second_line">';
1453
1454    if (item.objectType.toString().toLowerCase() === 'journal' && item.games.length > 0) {
1455        html += '<div class="diary_games">играл в ' + (item.games.length === 1 ? 'игру' : 'игры');
1456        $.each(item.games, function(index, game) {
1457            var photo = game.photoUrl === undefined ? 'https://tesera.ru/i/table_1.gif' : game.photoUrl;
1458            html += '<a href="/game/' + game.teseraId + '"><img src="' + photo + '" height="24" alt="' + game.title + '" title="' + game.title + '"></a>';
1459        });
1460        html += '</div>'
1461    }
1462
1463    html += '<div class="diary_comments"><img src="/img/icons/comments-small.gif" title="Комментариев"><span>' + item.commentsTotal + '</span></div>' +
1464        '</td></tr></table>';
1465
1466    html += '</div>';
1467
1468    return html;
1469}
1470
1471function diaryDraw(data)
1472{
1473    $('#diary #loader').remove();
1474
1475    $('#diary').append(data.html);
1476    $('#diary-count').html(data.total);
1477    $('#diary-pages').html(data.next);
1478}
1479
1480/*************************************
1481 * Games list
1482 * */
1483var gameSort;
1484var limitGamesOnPage = 30;
1485var gameTags = [];
1486var emptyPic = true;
1487var loadStyle = 'loader';
1488
1489////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1490/// REPORTS
1491function reportsLoad(limit, offset, callback, type, userAlias) {
1492    if (limit === undefined) {
1493        limit = 5;
1494    }
1495    if (offset === undefined) {
1496        offset = 0;
1497    }
1498    if (type === undefined) {
1499        type = 'full'
1500    }
1501
1502    let html = '';
1503    let next = '';
1504    let url = apiHost + '/reports'
1505
1506    if (typeof userAlias != 'undefined') {
1507        url += '/user/' + userAlias.toLowerCase()
1508    }
1509
1510    url += '?offset=' + offset + '&limit=' + limit;
1511
1512    $.ajax({
1513        type: "get",
1514        dataType: 'json',
1515        url: url,
1516        success: function (data, textStatus, request) {
1517            $.each(data, function (index, item) {
1518                if (type === 'simple') {
1519                    html += reportsItemSimpleDraw(item);
1520                } else {
1521                    html += reportsItemDraw(item);
1522                }
1523            });
1524        },
1525        complete: function (jqXHR) {
1526            var offset = jqXHR.getResponseHeader('x-offset');
1527            var totalPages = jqXHR.getResponseHeader('x-total-pages');
1528
1529            if (parseInt(offset) + 1 < totalPages) {
1530                next += '<a onclick="pageGamesLoad(' + limit + ', ' + (parseInt(offset) + 1) + '); return false;">Загрузить ещё</a>';
1531            }
1532
1533            if (html === '') {
1534                html = '<p class="market_empty">Отчёты не найдены</p>';
1535            }
1536
1537            callback({
1538                'html': html,
1539                'total': jqXHR.getResponseHeader('x-total-count'),
1540                'next': next
1541            });
1542        }
1543    });
1544}
1545
1546function reportsDraw(data) {
1547    $('.reports #loader').remove();
1548
1549    if (insertNewLoad === true) {
1550        $('.reports').html(data.html);
1551    } else {
1552        $('.reports').append(data.html);
1553    }
1554
1555    insertNewLoad = false;
1556
1557    $('#reports-count').html(data.total);
1558    $('#reports-pages').html(data.next);
1559}
1560
1561function reportsItemSimpleDraw(item) {
1562    let html = '<div class="comment" style="padding: 0 30px 10px 30px">';
1563    const logo = item.game.photoUrl === undefined ? 'https://tesera.ru/images/items/1175875,3/124x124xpa/photo1.' : item.game.photoUrl;
1564    const ava = item.user.avatarUrl === undefined ? 'https://tesera.ru/images/items/749890,22/24x24xpa/photo.' : item.user.avatarUrl
1565    const urlReport = mSiteUrl + '/reports/' + item.id
1566    const urlUser = siteUrl + '/user/' + item.user.teseraId
1567    const urlGame = siteUrl + '/game/' + item.game.teseraId
1568
1569    html += '<div class="ava">' +
1570        '<a href="' + urlUser + '"><img width="24" height="24" src="' + ava + '"></a>' +
1571        '</div>';
1572
1573    html += '<div class="text" style="overflow:hidden; word-wrap: break-word;  break-word: break-all;">' +
1574        '<span class="title">' +
1575        '<a href="' + urlUser + '">' + item.user.login + '</a> добавил <a href="' + urlReport + '">отчёт</a> по игре <a href="' + urlGame + '">' + item.game.title + '</a>' +
1576        '</span></div>';
1577
1578    html += '<div style="top: 0; right: 0; position: absolute;">' +
1579        '<a style="margin: 3px 0 0 auto; float: right;" href="' + urlGame + '"><img class="pic" src="' + logo + '" width="24" height="24" alt="' + item.game.title + '" title="' + item.game.title + '"></a>' +
1580        '</div>'
1581
1582    html += '<div class="clear"></div></div>';
1583
1584    return html;
1585}
1586
1587function reportsItemDraw(item) {
1588    var html = '<div class="feed_item" id="report-' + item.id + '">';
1589
1590    html += '<p class="feed_date">' + myDate(item.creationDateUtc) + '</p>';
1591
1592    html += '<table align="left" border="0" cellpadding="0" cellspacing="0" width="100%"><tr>';
1593
1594    var empty = '';
1595    var status = '';
1596
1597    if (item.game != undefined && item.game.photoUrl != undefined && item.game.photoUrl != '') {
1598        html += '<td width="70px" valign="top">' +
1599            '<div class="feed_firstImg" style="background: url(' + item.game.photoUrl + ') 0% 0% / 70px 70px no-repeat scroll content-box border-box transparent;">' + status + '</div>' +
1600            '</td>';
1601    } else {
1602        html += '<td width="70px">&nbsp;</td>';
1603    }
1604
1605    html += '<td valign="top">' +
1606        '<div class="feed_message">' + empty + '<b><a href="/game/' + item.game.teseraId + '">' + item.game.title + '</a></b><br />';
1607
1608    if (item.gameTime > 0) {
1609        let hour = Math.floor(item.gameTime / 60)
1610        let min = item.gameTime % 60
1611
1612        let time = hour > 0 ? hour + 'ч' : ''
1613        time += hour > 0 ? ' ' : ''
1614        time += (min + '').padEnd(2, 0) + 'м'
1615        html += 'Время партии ' + time + '; '
1616    }
1617    if (item.players.length > 0) {
1618        html += 'Игроков ' + item.players.length + '; '
1619    }
1620    if (item.resultType == 'Highest' && item.players.length > 0) {
1621        let points
1622        $.each(item.players, function(key, v) {
1623            if (v.isWinner == true) {
1624                points = v.points
1625            }
1626        })
1627
1628        let dx = points % 100;
1629        let word = dx == 1 ? 'очко' : (dx % 10 == 1 && dx > 20 ? 'очко' : ([2, 3, 4].includes(dx % 10) && (dx > 20 || dx < 10) ? 'очка' : 'очков'));
1630        if (typeof points != 'undefined' && points > 0) {
1631            html += 'Победный счёт ' + points + ' ' + word + '; '
1632        }
1633    }
1634
1635    if (item.description !== undefined && item.description.length > 0) {
1636        html += '<span><p class="feed_comment"><i>«' + item.description + '»</i></p></span>';
1637    } else {
1638        html += '<span></span>';
1639    }
1640
1641    html += '</div>' +
1642        '</td>';
1643
1644    html += '</tr></table></div>';
1645
1646    return html;
1647}
1648
1649
1650function gameReportsPhotosLoad(limit, offset, callback, gameAlias) {
1651    if (limit === undefined) {
1652        limit = 5;
1653    }
1654    if (offset === undefined) {
1655        offset = 0;
1656    }
1657
1658    let html = '';
1659    let next = '';
1660    let url = apiHost + '/reports/game/' + gameAlias + '/files?offset=' + offset + '&limit=' + limit
1661
1662    $.ajax({
1663        type: "get",
1664        dataType: 'json',
1665        url: url,
1666        success: function (data, textStatus, request) {
1667            $.each(data, function (index, item) {
1668                html += gameReportsPhotosItemDraw(item);
1669            });
1670        },
1671        complete: function (jqXHR) {
1672            var offset = jqXHR.getResponseHeader('x-offset');
1673            var totalPages = jqXHR.getResponseHeader('x-total-pages');
1674
1675            if (parseInt(offset) + 1 < totalPages) {
1676                next += '<a onclick="pageGamesLoad(' + limit + ', ' + (parseInt(offset) + 1) + '); return false;">Загрузить ещё</a>';
1677            }
1678
1679            if (html === '') {
1680                html = '<p class="market_empty">Фото не найдены</p>';
1681            }
1682
1683            callback({
1684                'html': html,
1685                'total': jqXHR.getResponseHeader('x-total-count'),
1686                'next': next
1687            });
1688        }
1689    });
1690}
1691
1692function gameReportsPhotosDraw(data) {
1693    $('#reports-photo #loader').remove();
1694
1695    if (insertNewLoad === true) {
1696        $('.reports-photo').html(data.html);
1697    } else {
1698        $('.reports-photo').append(data.html);
1699    }
1700
1701    insertNewLoad = false;
1702
1703    $('#reports-photo-count').html(data.total);
1704    $('#reports-photo-pages').html(data.next);
1705}
1706
1707function gameReportsPhotosItemDraw(item) {
1708    let html = '<div>'
1709
1710    html += '<a href="' + item.pathToFile + '" target="_blank"><img src="' + item.pathToTmb + '" /></a>'
1711    html += '</div>'
1712
1713    return html
1714}
1715////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1716
1717function gamesLoad(limit, offset, callback, type)
1718{
1719    if (limit === undefined) {
1720        limit = 30;
1721    }
1722    if (offset === undefined) {
1723        offset = 0;
1724    }
1725    if (gameSort === undefined) {
1726        gameSort = '-ratingn10';
1727    }
1728    if (type === undefined) {
1729        type = 'full'
1730    }
1731
1732    if (insertNewLoad === true) {
1733        $('#games-pages').html('');
1734        if (loadStyle === 'hide') {
1735            $('#games').append('<div class="loader_2" id="loader" style="height: ' + $('#games').height() + 'px"></div>');
1736        } else if (loadStyle === 'loader') {
1737            $('#games').html('<div class="loader" id="loader"><img src="/i/loader.gif"></div>');
1738        }
1739    }
1740
1741    var html = '';
1742    var next = '';
1743    var url = apiHost + '/games?offset=' + offset + '&limit=' + limit;
1744    if (gameSort === 'hotness') {
1745        url += '&sort=' + gameSort + '&type=' + gameSort;
1746    } else if (gameSort === 'hotnessbgg') {
1747        url += '&sort=hotness&type=' + gameSort;
1748    } else {
1749        url += '&sort=' + gameSort;
1750    }
1751
1752    if ($('#f-types').val() > 0) {
1753        gameTags.push(parseInt($('#f-types').val()));
1754    }
1755    if ($('#f-settings').val() > 0) {
1756        gameTags.push(parseInt($('#f-settings').val()));
1757    }
1758
1759    if (gameTags.length > 0) {
1760        url += '&tags=' + gameTags.join(',');
1761    }
1762
1763    if ($.trim($('#players-min').val()) != '' && parseInt($('#players-min').val()) > 0) {
1764        url += '&playersmin=' + parseInt($('#players-min').val());
1765    }
1766    if ($.trim($('#players-max').val()) != '' && parseInt($('#players-max').val()) > 0) {
1767        url += '&playersmax=' + parseInt($('#players-max').val());
1768    }
1769    if ($.trim($('#age-min').val()) != '' && parseInt($('#age-min').val()) > 0) {
1770        url += '&playersagemin=' + parseInt($('#age-min').val());
1771    }
1772    if ($.trim($('#time-min').val()) != '' && parseInt($('#time-min').val()) > 0) {
1773        url += '&playtimemin=' + parseInt($('#time-min').val());
1774    }
1775    if ($.trim($('#time-max').val()) != '' && parseInt($('#time-max').val()) > 0) {
1776        url += '&playtimemax=' + parseInt($('#time-max').val());
1777    }
1778
1779    if (emptyPic === false && gameSort === '-creationdateutc') {
1780        url += '&emptypicture=true';
1781    }
1782
1783    $.ajax({
1784        type: "get",
1785        dataType: 'json',
1786        url: url,
1787        success: function (data, textStatus, request) {
1788            $.each(data, function(index, item) {
1789                if (type === 'simple') {
1790                    html += gamesItemSimpleDraw(item);
1791                } else {
1792                    html += gamesItemDraw(item);
1793                }
1794            });
1795        },
1796        complete: function (jqXHR) {
1797            var offset = jqXHR.getResponseHeader('x-offset');
1798            var totalPages = jqXHR.getResponseHeader('x-total-pages');
1799
1800            if (parseInt(offset) + 1 < totalPages) {
1801                next += '<a onclick="pageGamesLoad(' + limit + ', ' + (parseInt(offset) + 1) + '); return false;">Загрузить ещё</a>';
1802            }
1803
1804            if (html === '') {
1805                html = '<p class="market_empty">Игры не найдены</p>';
1806            }
1807
1808            callback({
1809                'html': html,
1810                'total': jqXHR.getResponseHeader('x-total-count'),
1811                'next': next
1812            });
1813
1814            gameTags = []
1815        }
1816    });
1817}
1818
1819function pageGamesLoad(limit, offset)
1820{
1821    gamesLoad(limit, offset, function(data) {
1822        gamesDraw(data)
1823    });
1824}
1825
1826function gamesFilterSort(value, type)
1827{
1828    gamePosition = 0;
1829    insertNewLoad = true;
1830
1831    gameSort = value;
1832    gamesLoad(limitGamesOnPage, 0, function(data) {
1833        gamesDraw(data);
1834    }, type);
1835}
1836
1837var gamePosition = 0;
1838function gamesItemDraw(item)
1839{
1840    var html = '<div class="feed_item game_item">';
1841
1842    var addRating = '';
1843
1844    var bggVotes = item.bggNumVotes;
1845    if (item.bggNumVotes > 1000) {
1846        var x = item.bggNumVotes % 1000;
1847        var y = Math.floor(item.bggNumVotes / 1000);
1848        bggVotes = y + 'k';
1849    }
1850
1851    if (item.bggGeekRating > 0) {
1852        addRating += '<div class="game_rating r_first"><img src="/i/icn2.gif" width="24" height="29" alt="Рейтинг BoardGameGeek" title="Рейтинг BoardGameGeek"><span>' + item.bggGeekRating.toFixed(2) + '</span><sub>
1852' + bggVotes + '</sub></div>';
1853    } else {
1854        addRating += '<div class="game_rating r_first"><img src="/i/icn2.gif" width="24" height="29" alt="Рейтинг BoardGameGeek" title="Рейтинг BoardGameGeek"><span>-</span></div>';
1855    }
1856
1857    if (item.ratingUser > 0) {
1858        addRating += '<div class="game_rating r_second"><img src="/i/icn1.gif" width="24" height="29" alt="Средняя оценка" title="Средняя оценка"><span>' + item.ratingUser.toFixed(2) + '</span><sub>' + item.numVotes + '</sub></div>';
1859    } else {
1860        addRating += '<div class="game_rating r_second"><img src="/i/icn1.gif" width="24" height="29" alt="Средняя оценка" title="Средняя оценка"><span>-</span></div>';
1861    }
1862
1863    if (item.n10Rating > 0) {
1864        addRating += '<div class="game_rating r_third"><img src="/i/icn3.gif" width="24" height="29" alt="Рейтинг «Тесеры»" title="Рейтинг «Тесеры»"><span>' + item.n10Rating.toFixed(2) + '</span></div>';
1865    } else {
1866        addRating += '<div class="game_rating r_third"><img src="/i/icn3.gif" width="24" height="29" alt="Рейтинг «Тесеры»" title="Рейтинг «Тесеры»"><span>-</span></div>';
1867    }
1868
1869    var position = '';
1870    if (gameSort === '-ratingn10' || gameSort === '-ratinggeekbgg') {
1871        gamePosition = gamePosition + 1;
1872        position = '<div class="game_position"><span>' + gamePosition + '</span></div>';
1873    }
1874
1875    var href = '/game/' + item.alias;
1876    if (gameSort === 'hotnessbgg' && item.alias === undefined) {
1877        href = 'https://boardgamegeek.com/boardgame/' + item.bggId;
1878    }
1879
1880    html += '<h2 class="games_title">' + position + '<a href="' + href + '">' + item.title.substring(0, 40) + '</a>' + addRating + '</h2>';
1881    if (item.title2 !== undefined) {
1882        html += '<p class="games_date">' + item.title2 + ', ' + item.year + '</p>';
1883    } else if (item.year > 0) {
1884        html += '<p class="games_date">' + item.year + '</p>';
1885    } else {
1886        html += '<p class="games_date">&nbsp;</p>';
1887    }
1888
1889    var detailsRow = '';
1890    if (item.playersMin || item.playersMax) {
1891        detailsRow += '<div><img src="/i/icons/ic1.gif" width="21" height="21" align="absmiddle" title="Число игроков"><span>';
1892        if (item.playersMin !== item.playersMax && item.playersMin > 0 && (item.playersMax === undefined || item.playersMax <= 0)) {
1893            detailsRow += 'от ' + item.playersMin;
1894        } else if (item.playersMin !== item.playersMax && item.playersMax > 0 && item.playersMin === undefined) {
1895            detailsRow += 'до ' + item.playersMax;
1896        } else if (item.playersMin === item.playersMax) {
1897            detailsRow += item.playersMin;
1898        } else {
1899            detailsRow += item.playersMin + ' - ' + item.playersMax;
1900        }
1901        detailsRow += '</span></div>';
1902    }
1903    if (item.playersMinRecommend || item.playersMaxRecommend) {
1904        detailsRow += '<div><img src="/i/icons/ic2.gif" width="21" height="21" align="absmiddle" title="Рекомендуемое число игроков"><span>';
1905        if (item.playersMinRecommend !== item.playersMaxRecommend && item.playersMinRecommend > 0 && (item.playersMaxRecommend === undefined || item.playersMaxRecommend <= 0)) {
1906            detailsRow += 'от ' + item.playersMinRecommend;
1907        } else if (item.playersMinRecommend !== item.playersMaxRecommend && item.playersMaxRecommend > 0 && item.playersMinRecommend === undefined) {
1908            detailsRow += 'до ' + item.playersMaxRecommend;
1909        } else if (item.playersMinRecommend === item.playersMaxRecommend) {
1910            detailsRow += item.playersMinRecommend;
1911        } else {
1912            detailsRow += item.playersMinRecommend + ' - ' + item.playersMaxRecommend;
1913        }
1914        detailsRow += '</span></div>';
1915    }
1916    if (item.playersAgeMin > 0) {
1917        detailsRow += '<div><img src="/i/icons/ic5.gif" width="21" height="21" align="absmiddle" title="Возраст"><span>от ' + item.playersAgeMin + ' лет</span></div>';
1918    }
1919    if (item.timeToLearn > 0) {
1920        detailsRow += '<div><img src="/i/icons/ic3.gif" width="21" height="21" align="absmiddle" title="Время освоения"><span>' + item.timeToLearn + ' мин</span></div>';
1921    }
1922    if (item.playtimeMin || item.playtimeMax) {
1923        detailsRow += '<div><img src="/i/icons/ic4.gif" width="21" height="21" align="absmiddle" title="Время партии"><span>';
1924        if (item.playtimeMin !== item.playtimeMax && item.playtimeMin > 0 && (item.playtimeMax === undefined || item.playtimeMax <= 0)) {
1925            detailsRow += 'от ' + item.playtimeMin + ' мин';
1926        } else if (item.playtimeMin !== item.playtimeMax && item.playtimeMax > 0 && item.playtimeMin === undefined) {
1927            detailsRow += 'до ' + item.playtimeMax + ' мин';
1928        } else if (item.playtimeMin === item.playtimeMax) {
1929            detailsRow += item.playtimeMin;
1930        } else {
1931            detailsRow += item.playtimeMin + ' - ' + item.playtimeMax + ' мин';
1932        }
1933        detailsRow += '</span></div>';
1934    }
1935
1936
1937    var logo = item.photoUrl === undefined ? 'https://tesera.ru/images/items/1175875,3/124x124xpa/photo1.' : item.photoUrl;
1938
1939    if (detailsRow === '') {
1940        detailsRow = '&nbsp;';
1941    }
1942
1943    html += '<table class="game_details_table" align="left" border="0" cellpadding="0" cellspacing="0" width="100%">' +
1944        '<tr><td class="game_details">' + detailsRow + '</td></tr></table>';
1945
1946    if (item.descriptionShort === undefined) {
1947        item.descriptionShort = '&hellip;';
1948    }
1949
1950    html += '<table align="left" border="0" cellpadding="0" cellspacing="0" width="100%"><tr>
1950' +
1951        '<td width="70px" valign="top"><div class="feed_firstImg" style="background: url(' + logo + ') 0% 0% / 70px 70px no-repeat scroll content-box border-box transparent;"></div></td>' +
1952        '<td valign="top"><div class="feed_message">' + item.descriptionShort + '</div></td>' +
1953        '</tr>' +
1954        '<tr>' +
1955        '<td class="diary_second_line">';
1956
1957    html += '</td><td class="diary_second_line">';
1958
1959    var addComments = '';
1960    if (gameSort === 'hotness') {
1961        addComments += '<span class="game_new_comments">+' + item.commentsTotalNew + '</span>';
1962    }
1963
1964    html += '<div class="diary_comments"><img src="/img/icons/comments-small.gif" title="Комментариев"><span>' + item.commentsTotal + '</span>' + addComments + '</div>' +
1965        '</td></tr></table>';
1966
1967    html += '</div>';
1968
1969    return html;
1970}
1971
1972function gamesItemSimpleDraw(item)
1973{
1974    var html = '<div class="c_game_item mb0">';
1975    var logo = item.photoUrl === undefined ? 'https://tesera.ru/images/items/1175875,3/124x124xpa/photo1.' : item.photoUrl;
1976    let href = item.alias !== undefined ? '/game/' + item.alias : (item.bggId > 0 ? 'https://boardgamegeek.com/boardgame/' + item.bggId : '#');
1977
1978    html += '<a class="c_game_img_wrap" href="' + href + '"><img class="c_game_img2" src="' + logo + '"/></a>';
1979
1980    var addComments = '';
1981    if (gameSort === 'hotness') {
1982        addComments += '<span class="main_game_new_comments"><img src="/i/smallexp_comments.gif"/>+' + item.commentsTotalNew + '</span>';
1983    }
1984
1985    var title = item.title.length > 34 ? item.title.substring(0, 30) + '&hellip;' : item.title;
1986    html += '<p class="c_game_title"><a title="' + item.title + '" href="/game/' + item.alias + '" >' + title + '</a></p>';
1987    html += addComments;
1988
1989    if (gameSort === '-creationdateutc') {
1990
1991    } else if (gameSort === 'hotnessbgg') {
1992        html += '<div class="c_game_rating" title="Рейтинг BoardGameGeek"><img src="/i/bgg_16.gif"><span>' + (item.bggRating <= 0 ? '-' : item.bggRating.toPrecision(3)) + '</span></div>';
1993    } else {
1994        html += '<div class="c_game_rating" title="Рейтинг Тесеры"><img src="/i/up-r2.gif"><span>' + (item.n10Rating <= 0 ? '-' : item.n10Rating) + '</span></div>';
1995    }
1996
1997    html += '</div>';
1998
1999    return html;
2000}
2001
2002function gamesDraw(data)
2003{
2004    $('#games #loader').remove();
2005
2006    if (insertNewLoad === true) {
2007        $('#games').html(data.html);
2008    } else {
2009        $('#games').append(data.html);
2010    }
2011
2012    insertNewLoad = false;
2013
2014    $('#games-count').html(data.total);
2015    $('#games-pages').html(data.next);
2016}
2017
2018var tagIds = {};
2019function gamesFiltersLoad(type, limit, offset, callback)
2020{
2021
2022    if (offset === undefined) {
2023        offset = 0;
2024    }
2025    if (limit === undefined) {
2026        limit = 100;
2027    }
2028
2029    if (offset === 0) {
2030        var html = '<option value="0">-</option>';
2031        tagIds[type] = {};
2032    }
2033
2034    $.ajax({
2035        type: "get",
2036        dataType: 'json',
2037        url: apiHost + '/tags/' + type + '?offset=' + offset + '&limit=' + limit,
2038        success: function (data) {
2039            $.each(data, function(index, item) {
2040                if (item.parentId === undefined) {
2041                    tagIds[type]['\'' + item.id + '\''] = {
2042                        'ids': [],
2043                        'id': item.id,
2044                        'title': item.name.charAt(0).toUpperCase() + item.name.substr(1)
2045                    }
2046                }
2047            });
2048
2049            $.each(data, function(index, item) {
2050                if (item.parentId !== undefined) {
2051                    tagIds[type]['\'' + item.parentId + '\'']['ids'].push({
2052                        'id': item.id,
2053                        'title': item.name.charAt(0).toUpperCase() + item.name.substr(1)
2054                    });
2055                }
2056            });
2057        },
2058        complete: function (jqXHR) {
2059            var offset = jqXHR.getResponseHeader('x-offset');
2060            var totalPages = jqXHR.getResponseHeader('x-total-pages');
2061
2062            if (parseInt(offset) + 1 < totalPages) {
2063                //
2064            } else {
2065                $.each(tagIds[type], function(index, item) {
2066                    if (item.ids.length > 0) {
2067                        html += '<option value="' + item.id + '">' + item.title + '</option>';
2068                        html += '<optgroup label="' + item.title + '" class="hide">';
2069                        $.each(item.ids, function(i2, item2) {
2070                            html += '<option value="' + item2.id + '">' + item2.title + '</option>';
2071                        });
2072                        html += '</optgroup>';
2073                    } else {
2074                        html += '<option value="' + item.id + '">' + item.title + '</option>';
2075                    }
2076                });
2077
2078                $('#f-' + type).html(html);
2079                callback();
2080            }
2081        }
2082    });
2083}
2084
2085function safeClear()
2086{
2087    gamePosition = 0;
2088    insertNewLoad = true;
2089    gameTags = [];
2090}
2091
2092function resetGamesFilters()
2093{
2094    safeClear();
2095    gameSort = '-ratingn10';
2096    $('#f-sort').val('-ratingn10');
2097
2098    $('#f-types').val(0);
2099    $('#f-settings').val(0);
2100    $('#players-min').val('');
2101    $('#players-max').val('');
2102    $('#age-min').val('');
2103    $('#time-min').val('');
2104    $('#time-max').val('');
2105
2106    gamesLoad(30, 0, function(data) {
2107        gamesDraw(data);
2108    });
2109}
2110
2111/*************************************
2112 * Collection
2113 * */
2114var collectionSort;
2115var ownDraw = {
2116    'limitRecord': 30,
2117    'pagesView': true,
2118    'myPage': false
2119};
2120
2121
2122function collectionGameInfo(relId)
2123{
2124    var html = '<div class="popuper" type="game_rel_comments0" style="z-index: 10000; margin-left: -284px; margin-top: -113px; display: block;" id="collection-game-' + relId + '"><div class="popup-top">&nbsp;</div>' +
2125        '<div class="popup">' +
2126        '<h3>' + $('#cg-' + relId + '-title').attr('title') + '</h3>' +
2127        '<img class="close" src="/img/p_close.gif" border="0" onclick="closePopup(\'collection-game-' + relId + '\'); return false;">' +
2128        '<br>' +
2129        '<form id="rel">' +
2130        '<table cellpadding="0" cellspacing="0" border="0" class="form">' +
2131        '<tbody><tr>' +
2132        '<td>' +
2133        '<h3 style="width:100%; margin-left: 10px;"><i>«' + nl2br($('#cg-' + relId + '-comment').html()) + '»</i></h3>' +
2134        '</td>' +
2135        '</tr>' +
2136        '<tr><td colspan="2"><hr></td></tr>';
2137
2138    if ($('#cg-' + relId).attr('data-edate') !== 'undefined') {
2139        html += '<tr><td style="white-space:nowrap">Игра приобретена<br><h3 style="margin-left: 10px">' + myDate($('#cg-' + relId).attr('data-edate'), false) + '</h3></td></tr>';
2140    }
2141
2142    if ($('#cg-' + relId).attr('data-cdate') !== 'undefined') {
2143        html += '<tr><td style="white-space:nowrap">Игра добавлена<br><h3 style="margin-left: 10px">' + myDate($('#cg-' + relId).attr('data-cdate')) + '</h3></td></tr>';
2144    }
2145
2146    html += '</tbody></table>' +
2147        '</form>' +
2148        '</div>' +
2149        '<div class="popup-bottom">&nbsp;</div>' +
2150        '</div>';
2151
2152    $('#popup-area').html(html);
2153}
2154
2155function collectionGamePlayInfo(relId)
2156{
2157    var html = '<div class="popuper" type="game_rel_comments0" style="z-index: 10000; margin-left: -284px; margin-top: -113px; display: block;" id="collection-game-p-' + relId + '"><div class="popup-top">&nbsp;</div>' +
2158        '<div class="popup">' +
2159        '<h3>' + $('#cg-' + relId + '-title').attr('title') + '</h3>' +
2160        '<img class="close" src="/img/p_close.gif" border="0" onclick="closePopup(\'collection-game-p-' + relId + '\'); return false;">' +
2161        '<br>' +
2162        '<form id="rel">' +
2163        '<table cellpadding="0" cellspacing="0" border="0" class="form">' +
2164        '<tbody><tr>' +
2165        '<td>' +
2166        '<h3 style="width:100%; margin-left: 10px;"><i>«' + $('#cg-play-' + relId + '-comment').html() + '»</i></h3>' +
2167        '</td>' +
2168        '</tr>' +
2169        '<tr><td colspan="2"><hr></td></tr>';
2170
2171    if ($('#cg-' + relId).attr('data-cdate') !== undefined) {
2172        html += '<tr><td style="white-space:nowrap">Игра добавлена в список<br><h3 style="margin-left: 10px">' + myDate($('#cg-' + relId).attr('data-cdate')) + '</h3></td></tr>';
2173    }
2174
2175    html += '</tbody></table>' +
2176        '</form>' +
2177        '</div>' +
2178        '<div class="popup-bottom">&nbsp;</div>' +
2179        '</div>';
2180
2181    $('#popup-area').html(html);
2182}
2183
2184function closePopup(id, type)
2185{
2186    if (type === 'like') {
2187        $('[id ^= ' + id + ']').remove();
2188    } else {
2189        $('#' + id).remove();
2190    }
2191}
2192
2193/*
2194 * del game from collection on gamepage
2195 */
2196function delGameCollection(alias, cAlias, check, callback)
2197{
2198    if (check === undefined) {
2199        check = true;
2200    }
2201
2202    if (check === true) {
2203        if (!confirm('Вы уверены что хотите удалить игру из списка?')) {
2204            return false;
2205        }
2206    }
2207
2208    $.ajax({
2209        type: "delete",
2210        url: apiHost + '/collections/base/' + cAlias + '/' + alias,
2211        beforeSend: function (xhr) {
2212            xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
2213        },
2214        complete: function (jqXHR) {
2215
2216            if (callback !== undefined) {
2217                callback();
2218            }
2219        }
2220    });
2221}
2222
2223function addGameToPlay(gameId)
2224{
2225    var id = 'popuper_' + gameId + '_rel_link_user';
2226    var html = '<div class="popuper" type="game_rel_want_use_me0" style="z-index: 10000; margin-left: -284px; margin-top: -185px; display: block;" id="' + id + '"><div class="popup-top">&nbsp;</div>' +
2227        '<div class="popup">' +
2228        '<h5>' + $('#list-el-' + gameId).attr('title') + '</h5>' +
2229        '<h2>Желаете сыграть в игру?</h2>' +
2230        '<img class="close" src="/img/p_close.gif" border="0" onclick="closePopup(\'' + id + '\')"><br>' +
2231        '<form id="rel" method="post" action="/game_rel_want_use_me0/" enctype="multipart/form-data">' +
2232        '<input type="hidden" name="id" value="0">' +
2233        '<input type="hidden" name="rel_save" value="1">' +
2234        '<input type="hidden" name="MAX_FILE_SIZE" value="10000000">' +
2235        '<input type="hidden" id="linked_id" value="' + gameId + '">' +
2236        '<table cellpadding="0" cellspacing="0" border="0" class="form">' +
2237        '<tbody><tr>' +
2238        '<td width="160" valign="top"><h3>ваш комментарий</h3></td>' +
2239        '<td>' +
2240        '<textarea name="rel_comments" style="width:100%; height: 130px"></textarea>' +
2241        '</td>' +
2242        '</tr>' +
2243        '<tr><td colspan="2"><hr></td></tr>' +
2244        '<tr><td colspan="2">' +
2245        '<div id="ajx_loader" class="ajx_loader"></div>' +
2246        '</td></tr>' +
2247        '<tr><td colspan="2">' +
2248        '<div form="rel" class="submit save green create" onclick="addGameCollection(\'toplay\', function() { updateControl(\'' + gameId + '\', 1); closePopup(\'' + id + '\'); })"><span>отправить</span></div>' +
2249        '</td></tr>' +
2250        '</tbody>' +
2251        '</table>' +
2252        '</form>' +
2253        '</div>' +
2254        '<div class="popup-bottom">&nbsp;</div>' +
2255        '</div>';
2256
2257    $('#popup-area').html(html);
2258}
2259
2260function addGameBuy(gameId)
2261{
2262    var id = 'popuper_' + gameId + '_rel_link_user';
2263    var html = '<div class="popuper" type="game_rel_buyer_me0" style="z-index: 10000; margin-left: -284px; margin-top: -264px; display: block;" id="' + id + '"><div class="popup-top">&nbsp;</div>' +
2264        '<div class="popup">' +
2265        '<h5>' + $('#list-el-' + gameId).attr('title') + '</h5>' +
2266        '<h2>желаете приобрести игру?</h2>' +
2267        '<h5 style="color:red">Для размещения объявления на "Барахолке" — обязательно укажите состояние игры и/или её цену.</h5>' +
2268        '<img class="close" src="/img/p_close.gif" border="0" onclick="closePopup(\'' + id + '\')">' +
2269        '<br>' +
2270        '<form id="rel" method="post" action="/game_rel_buyer_me0/" enctype="multipart/form-data">' +
2271        '<input type="hidden" name="id" value="">' +
2272        '<input type="hidden" name="rel_save" value="1">' +
2273        '<input type="hidden" name="MAX_FILE_SIZE" value="10000000">' +
2274        '<input type="hidden" id="linked_id" value="' + gameId + '">' +
2275        '<table cellpadding="0" cellspacing="0" border="0" class="form">' +
2276        '<tbody><tr><td colspan="2"><hr></td></tr>' +
2277        '<tr>' +
2278        '<td width="160" valign="top"><h3>ваш комментарий</h3></td>' +
2279        '<td>' +
2280        '<textarea name="rel_comments" id="rel_comments" style="width:100%; height: 130px"></textarea>' +
2281        '</td>' +
2282        '</tr>' +
2283        '<tr><td colspan="2">&nbsp;</td></tr>' +
2284        '<tr>' +
2285        '<td width="160">&nbsp;</td>' +
2286        '<td>' +
2287        '<input style="float: left; margin-right: 10px" type="checkbox" name="rel_is_new" id="rel_is_new">' +
2288        '<h3 style="float: left; margin-right: 20px"><label for="rel_is_new">новую?</label></h3>' +
2289        '<input style="float: left; margin-right: 10px" type="checkbox" name="rel_is_old" id="rel_is_old">' +
2290        '<h3><label for="rel_is_old">б.у.?</label></h3>' +
2291        '</td>' +
2292        '</tr>' +
2293        '<tr><td colspan="2">&nbsp;</td></tr>' +
2294        '<tr>' +
2295        '<td width="160" valign="top"><h3>пожелания по цене</h3></td>' +
2296        '<td>' +
2297        '<input type="text" name="rel_price" value="" maxlength="10" style="width: 80px">' +
2298        '</td>' +
2299        '</tr>' +
2300        '<tr><td colspan="2">' +
2301        '<div id="ajx_loader" class="ajx_loader"></div>' +
2302        '</td></tr>' +
2303        '<tr><td colspan="2">' +
2304        '<h5 style="color:red">Укажите, по возможности, в каком состоянии, по какой максимальной цене, каким способом вы хотели бы получить игру.</h5>' +
2305        '</td></tr>' +
2306        '<tr><td colspan="2"><hr></td></tr>' +
2307        '<tr><td colspan="2">' +
2308        '<div form="rel" class="submit save green create" onclick="addPurchase(function(data) { updateControl(\'' + gameId + '\', 3, data.id); closePopup(\'' + id + '\'); });"><span>отправить</span></div>' +
2309        '</td></tr>' +
2310        '</tbody></table>' +
2311        '</form>' +
2312        '</div>' +
2313        '<div class="popup-bottom">&nbsp;</div>' +
2314        '</div>';
2315
2316    $('#popup-area').html(html);
2317}
2318
2319var toggleAdd = false;
2320function addGameCollection(alias, callback)
2321{
2322    var gameAlias = $('#linked_id').val();
2323    var comment = $('textarea[name="rel_comments"]').val();
2324    var eventDate = $('input[name="rel_date"]').val();
2325    var id = $('input[name="id"]').val();
2326
2327    if (gameAlias === "0") {
2328        alert('Игра не выбрана!');
2329        return false;
2330    } else if (toggleAdd === true) {
2331        return false;
2332    }
2333
2334    toggleAdd = true;
2335    if (id !== undefined && id > 0) {
2336        $.ajax({
2337            type: "put",
2338            dataType: 'json',
2339            url: apiHost + '/collections/base/' + alias + '/' + gameAlias,
2340            contentType: "application/json",
2341            data: JSON.stringify({
2342                comment: comment,
2343                eventDate: eventDate
2344            }),
2345            beforeSend: function (xhr) {
2346                xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
2347            },
2348            complete: function () {
2349                callback();
2350                toggleAdd = false;
2351            }
2352        });
2353    } else {
2354        $.ajax({
2355            type: "post",
2356            dataType: 'json',
2357            url: apiHost + '/collections/base/' + alias,
2358            contentType: "application/json",
2359            data: JSON.stringify({
2360                comment: comment,
2361                alias: gameAlias,
2362                eventDate: eventDate
2363            }),
2364            beforeSend: function (xhr) {
2365                xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
2366            },
2367            complete: function () {
2368                callback();
2369                toggleAdd = false;
2370            }
2371        });
2372    }
2373}
2374
2375function addClearGameCollection(gameAlias, alias, callback)
2376{
2377    if (toggleAdd === true) {
2378        return false;
2379    }
2380
2381    toggleAdd = true;
2382    $.ajax({
2383        type: "post",
2384        dataType: 'json',
2385        url: apiHost + '/collections/base/' + alias,
2386        contentType: "application/json",
2387        data: JSON.stringify({
2388            alias: gameAlias
2389        }),
2390        beforeSend: function (xhr) {
2391            xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
2392        },
2393        complete: function () {
2394            callback();
2395            toggleAdd = false;
2396        }
2397    });
2398}
2399
2400
2401
2402function gameListsUpdate(gameId)
2403{
2404    let path = window.location.pathname.search('wanters') > 0 ? 'game_want_use_users_block' : 'game_link_users_block';
2405    let id = window.location.pathname.search('wanters') > 0 ? '_game_want_use_users' : '_rel_link_game_users';
2406    $.ajax({
2407        type: "get",
2408        url: siteUrl + '/' + path + '/' + gameId + '/?rand=' + Math.ceil(Math.random() * 10000000) + '&v=1',
2409        success: function (data) {
2410            $('#' + gameId + id).html(data);
2411        }
2412    });
2413}
2414
2415function addPlayedPopup(userId)
2416{
2417    $.ajax({
2418        type: "get",
2419        url: siteUrl + '/user_rel_played_game0/?rand=' + Math.ceil(Math.random() * 10000000),
2420        success: function (data) {
2421            $('#popup-area').html(data);
2422        }
2423    });
2424}
2425
2426/*************************************
2427 * Collection TOP
2428 * */
2429function addGameRating(alias, gameplay, depth, orig, realiz)
2430{
2431    $.ajax({
2432        type: "post",
2433        dataType: 'json',
2434        url: apiHost + '/games/' + alias + '/ratingstesera',
2435        contentType: "application/json",
2436        data: JSON.stringify({
2437            comment: "",
2438            ratinggameplay: gameplay,
2439            ratingdepth: depth,
2440            ratingoriginality: orig,
2441            ratingrealization: realiz
2442        }),
2443        beforeSend: function (xhr) {
2444            xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
2445        }
2446    });
2447}
2448
2449function delGameRating(alias)
2450{
2451    $.ajax({
2452        type: "delete",
2453        dataType: 'json',
2454        url: apiHost + '/games/' + alias + '/ratings',
2455        contentType: "application/json",
2456        beforeSend: function (xhr) {
2457            xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
2458        }
2459    });
2460}
2461
2462/*************************************
2463 * Users
2464 */
2465var usersSort;
2466var limitUsersOnPage = 30;
2467
2468function usersLoad(limit, offset, callback)
2469{
2470    if (limit === undefined) {
2471        limit = 30;
2472    }
2473    if (offset === undefined) {
2474        offset = 0;
2475    }
2476
2477
2478    var html = '';
2479    var next = '';
2480    var url = apiHost + '/user/list?offset=' + offset + '&limit=' + limit;
2481    if (usersSort !== undefined) {
2482        url += '&sort=' + usersSort;
2483    }
2484    if (filterCity !== undefined) {
2485        url += '&city=' + filterCity;
2486    }
2487
2488    $.ajax({
2489        type: "get",
2490        dataType: 'json',
2491        url: url,
2492        success: function (data, textStatus, request) {
2493            $.each(data, function(index, item) {
2494                html += usersItemDraw(item);
2495            });
2496        },
2497        complete: function (jqXHR) {
2498            var offset = jqXHR.getResponseHeader('x-offset');
2499            var totalPages = jqXHR.getResponseHeader('x-total-pages');
2500
2501            if (parseInt(offset) + 1 < totalPages) {
2502                next += '<a onclick="pageUsersLoad(' + limit + ', ' + (parseInt(offset) + 1) + '); return false;">Загрузить ещё</a>';
2503            }
2504
2505            if (html === '') {
2506                html = '<p class="market_empty">Пользователи не найдены</p>';
2507            }
2508
2509            callback({
2510                'html': html,
2511                'total': jqXHR.getResponseHeader('x-total-count'),
2512                'next': next
2513            });
2514        }
2515    });
2516}
2517
2518function pageUsersLoad(limit, offset)
2519{
2520    usersLoad(limit, offset, function(data) {
2521        usersDraw(data)
2522    });
2523}
2524
2525function usersDraw(data)
2526{
2527    $('#users #loader').remove();
2528
2529    if (insertNewLoad === true) {
2530        $('#users').html(data.html);
2531    } else {
2532        $('#users').append(data.html);
2533    }
2534
2535    insertNewLoad = false;
2536
2537    $('#users-count').html(data.total);
2538    $('#users-pages').html(data.next);
2539}
2540
2541function usersFilterSort(value)
2542{
2543    insertNewLoad = true;
2544
2545    usersSort = value;
2546    usersLoad(limitUsersOnPage, 0, function(data) {
2547        usersDraw(data);
2548    });
2549}
2550
2551function usersFilterCity(teseraId, alias, title)
2552{
2553    $('#f-city').val(title);
2554    $('#f-city').css('color', '#0e8400');
2555
2556    insertNewLoad = true;
2557
2558    filterCity = teseraId;
2559    usersLoad(limitUsersOnPage, 0, function(data) {
2560        usersDraw(data);
2561    });
2562}
2563
2564function resetUsersFilters()
2565{
2566    insertNewLoad = true;
2567
2568    $('#f-country').val('');
2569    $('#f-country').css('color', '#000');
2570    $('#f-city').val('');
2571    $('#f-city').css('color', '#000');
2572    $('#f-sort').val('rating');
2573    usersSort = undefined;
2574    filterCity = undefined;
2575
2576    usersLoad(limitUsersOnPage, 0, function(data) {
2577        usersDraw(data);
2578    });
2579}
2580
2581function usersItemDraw(item)
2582{
2583    var html = '<div class="feed_item">';
2584
2585    html += '';
2586
2587    var addRole = '';
2588//    if (item.role !== undefined) {
2589//        addRole += '<div class="diary_rating"><img src="" width="24" height="29" alt="" title=""></div>';
2590//    }
2591
2592    var location = '';
2593    if (item.country !== undefined && item.city !== undefined && filterCity === undefined) {
2594        location += item.country.name + ', <a href="/location/' + item.country.teseraId + '/' + item.city.teseraId + '">' + item.city.name + '</a>';
2595    } else if (item.city !== undefined && filterCity === undefined && item.city.name != "0") {
2596        location += '' + item.city.name + '';
2597    }
2598
2599    let author = item?.name || item?.login || ''
2600    if (author.toString().toLowerCase() !== item.login.toString().toLowerCase()) {
2601        author += ' (' + item.login + ')';
2602    }
2603    html += '<h2 class="diary_title"><a href="/user/' + item.teseraId + '">' + author + '</a>' + addRole + '</h2>';
2604
2605    var ava = item.avatarUrl === undefined ? 'https://tesera.ru/images/items/1366656,22/125x125/photo.' : item.avatarUrl;
2606    html += '<table align="left" border="0" cellpadding="0" cellspacing="0" width="100%">' +
2607        '<tr>' +
2608        '<td width="100px" valign="top"><div class="u_avatar" style="background: url(' + ava + ') 0% 0% / 100px 100px no-repeat scroll content-box border-box transparent;"></div></td>';
2609
2610
2611    html += '<td valign="top">';
2612    html += '<div class="u_location"><span>Зарегистрирован' + (item.gender === 'Female' ? 'а' : '') + ' ' + myDate(item.creationDateUtc, false) + '</span>';
2613    if (location !== '') {
2614        html += ', ' + location + '';
2615    }
2616    html += '</div>';
2617
2618    if (item.comment !== undefined) {
2619        html += '<div class="feed_message">' + item.comment + '</div></td>';
2620    }
2621
2622    html += '</tr>' +
2623        '<tr>' +
2624        '<td class="u_second_line" colspan="2">';
2625
2626    html += '<div class="u_user_general_info"><img src="/i/icn3.gif" width="24" height="29" title="Рейтинг"><span>' + item.rating + '</span></div>';
2627    html += '<div class="u_user_general_info">
2627<img src="/i/xc-e.gif" width="24" height="29" title="Стаж"><span>' + item.experience + '</span></div>';
2628    html += '<div class="u_user_general_info"><img src="/i/xc-w1.gif" width="24" height="29" title="Коллекция"><span>' + item.collectionCount + '</span></div>';
2629
2630
2631    html += '<div class="u_right_info">';
2632    html += '<div><img src="/img/miniicons/games.gif" title="Игр"><span>' + item.gamesAdded + '</span></div>';
2633    html += '<div><img src="/img/miniicons/news.gif" title="Новостей"><span>' + item.newsAdded + '</span></div>';
2634    html += '<div><img src="/img/miniicons/articles.gif" title="Статей"><span>' + item.articlesAdded + '</span></div>';
2635    html += '<div><img src="/img/miniicons/gamediary3.gif" title="Отчётов"><span>' + item.journalsAdded + '</span></div>';
2636    html += '<div><img src="/img/miniicons/thoughts3.gif" title="Мыслей"><span>' + item.thoughtsAdded + '</span></div>';
2637    html += '</div>';
2638//	html += '<div class="diary_comments"><img src="/img/miniicons/videos.gif" title="Комментариев"><span>' + item.videosAdded + '</span></div>';
2639
2640    html += '</td></tr></table>';
2641
2642    html += '</div>';
2643
2644    return html;
2645}
2646
2647function getUserInfo(alias, callback)
2648{
2649    $.ajax({
2650        type: "get",
2651        dataType: 'json',
2652        url: apiHost + '/user/' + alias,
2653        success: function (data) {
2654            callback(data)
2655        }
2656    });
2657}
2658
2659/**
2660 * Index
2661 */
2662function newLogout()
2663{
2664    $.ajax({
2665        type: "post",
2666        url: siteUrl + '',
2667        data: {
2668            logout: 1
2669        },
2670        complete: function () {
2671            localStorage.removeItem('hash');
2672            $.cookie("auth._token.local", null, {domain: '.tesera.ru', path: '/', expires: -1});
2673            window.location = siteUrl;
2674        }
2675    });
2676}
2677
2678function newLogin(login, password, ip)
2679{
2680    $.ajax({
2681        type: "post",
2682        url: siteUrl + '?ajax=123',
2683        data: {
2684            auth_login: login,
2685            auth_password: MD5(password),
2686            ipaddress: ip,
2687            submit: 'OK'
2688        },
2689        success: function (data) {
2690            if (!isNaN(parseFloat(data)) && isFinite(data)) {
2691                window.location = siteUrl + '/user/';
2692            } else {
2693                pp_alert(data);
2694            }
2695        }
2696    });
2697}
2698
2699function nl2br(str, is_xhtml) {
2700    var breakTag = (is_xhtml || typeof is_xhtml === 'undefined') ? '<br />' : '<br>';
2701    return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1'+ breakTag +'$2');
2702}
2703
2704function loadLoginForm()
2705{
2706    $.ajax({
2707        type: "get",
2708        url: siteUrl + '/user_auth_block',
2709        success: function (data, textStatus, request) {
2710            $('#user_auth').html(data);
2711        }
2712    });
2713}
2714
2715/* Game lists */
2716function simpleListsItemDrawList(item)
2717{
2718    var html = '';
2719
2720    var title = item.title;
2721    title = title.replace('Лучшие', '').replace('игры', '').replace(/\s+/, ' ').trim().replace(/^\-/, '').replace(/\-$/, '').trim();
2722    title = title.charAt(0).toUpperCase() + title.substr(1);
2723
2724    html += '<li><a href="/list/' + item.alias + '">' + (title.length > 34 ? title.substring(0, 30) + '&hellip;' : title) + '</a></li>';
2725
2726    return html;
2727}
2728function fullListsItemDrawList(item)
2729{
2730    var html = '<div class="feed_item">';
2731
2732    html += '<p class="diary_date" >Обновлён ' + myDate(item.modificationDateUtc, false, 2) + ' (создан ' + myDate(item.creationDateUtc, false, 2) + ')</p>';
2733
2734    var rating = item.ratingUser > 0 ? item.ratingUser : '-';
2735    var items = item.itemsTotal > 0 ? item.itemsTotal : '-';
2736    var comments = item.commentsTotal > 0 ? item.commentsTotal : '-';
2737
2738    var addLine = '<div class="diary_rating r_first"><img src="/i/icn1.gif" width="24" height="29" alt="Оценка пользователей" title="Оценка пользователей"><span>' + rating + '</span></div>';
2739    addLine += '<div class="diary_rating r_second"><img src="/img/icons/book-small.gif" width="25" height="21" alt="Элементов" title="Элементов"><span>' + items + '</span></div>';
2740    addLine += '<div class="diary_rating r_third"><img src="/img/icons/comments-small.gif" width="25" height="19" alt="Комментариев" title="Комментариев"><span>' + comments + '</span></div>';
2741
2742    html += '<h2 class="diary_title"><a href="/list/' + item.alias + '">' + item.title + '</a>' + addLine + '</h2>';
2743
2744    var addTitle = item.title2 !== undefined ? '<b>' + item.title2 + '</b><br />' : '';
2745    var pic = item.photoUrl === undefined ? 'https://tesera.ru/images/items/1396329,65/125x125xpa/photo.' : item.photoUrl.replace('photo1.jpg', '200x200xca/photo1.jpg');
2746    html += '<table align="left" border="0" cellpadding="0" cellspacing="0" width="100%">' +
2747        '<tr>' +
2748        '<td width="125px" valign="top" class="book_pic"><img src="' + pic + '"/></td>' +
2749        '<td valign="top"><div class="feed_message">' + addTitle + (item.descriptionShort !== undefined ? item.descriptionShort : '&hellip;') + '</div></td>' +
2750        '</tr>' +
2751        '<tr>' +
2752        '<td class="diary_second_line">';
2753
2754    html += '</td><td class="diary_second_line">';
2755
2756    html += '</td></tr></table>';
2757    html += '</div>';
2758
2759    return html;
2760}
2761function updateListElement(elementId, callback)
2762{
2763    $.ajax({
2764        type: "put",
2765        dataType: 'json',
2766        url: apiHost + '/lists/' + elementId,
2767        contentType: "application/json",
2768        data: JSON.stringify({
2769            description: tinyMCE.activeEditor.getContent()
2770        }),
2771        beforeSend: function (xhr) {
2772            xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
2773        },
2774        complete: function () {
2775            callback();
2776        }
2777    });
2778}
2779function addListElement(alias, callback)
2780{
2781    if (alias === undefined) {
2782        alias = $('#element_pid').val();
2783    }
2784
2785    $.ajax({
2786        type: "post",
2787        dataType: 'json',
2788        url: apiHost + '/lists/' + alias,
2789        contentType: "application/json",
2790        data: JSON.stringify({
2791            description: "",
2792            objectid: $('#linked_id').val()
2793        }),
2794        beforeSend: function (xhr) {
2795            xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
2796        },
2797        complete: function () {
2798            callback();
2799        }
2800    });
2801}
2802
2803function updateControl(gameId, type, itemId)
2804{
2805    var html = '';
2806    console.log(type, gameId);
2807    if (type === 1) {
2808        html += '<a href="#" data-type="1" onclick="delGameCollection(\'' + gameId + '\', \'toplay\', false, function() { updateControl(\'' + gameId + '\', 2) }); return false;" class="no_subscribe" id="control-subs">Хочу сыграть</a>';
2809        $('#cntrl-toplay-' + gameId).html(html);
2810    } else if (type === 2) {
2811        html += '<a href="#" data-type="2" onclick="addGameToPlay(\'' + gameId + '\'); return false;" class="is_subscribe" id="control-subs">Хочу сыграть</a>';
2812        $('#cntrl-toplay-' + gameId).html(html);
2813    } else if (type === 3) {
2814        html += '<a href="#" data-type="3" onclick="delAdv(' + itemId + ', \'purchases\', false, false, function() { updateControl(\'' + gameId  + '\', 4) }); return false;" class="no_subscribe">Хочу купить</a>';
2815        $('#cntrl-buy-' + gameId).html(html);
2816    } else if (type === 4) {
2817        html += '<a href="#" data-type="4" onclick="addGameBuy(\'' + gameId + '\'); return false;" class="is_subscribe">Хочу купить</a>';
2818        $('#cntrl-buy-' + gameId).html(html);
2819    }
2820}
2821function elementListsItemDrawList(data, _this)
2822{
2823    var html = '';
2824
2825    var item = data.game;
2826
2827    if (item === undefined) {
2828        return '';
2829    }
2830
2831    html = '<div class="feed_item game_item" id="sc-' + data.itemTeseraId + '">';
2832
2833    var addRating = '';
2834
2835    var bggVotes = item.bggNumVotes;
2836    if (item.bggNumVotes > 1000) {
2837        var x = item.bggNumVotes % 1000;
2838        var y = Math.floor(item.bggNumVotes / 1000);
2839        bggVotes = y + 'k';
2840    }
2841
2842    if (item.bggGeekRating > 0) {
2843        addRating += '<div class="game_rating r_first"><img src="/i/icn2.gif" width="24" height="29" alt="Рейтинг BoardGameGeek" title="Рейтинг BoardGameGeek"><span>' + item.bggGeekRating.toFixed(2) + '</span><sub>' + bggVotes + '</sub></div>';
2844    } else {
2845        addRating += '<div class="game_rating r_first"><img src="/i/icn2.gif" width="24" height="29" alt="Рейтинг BoardGameGeek" title="Рейтинг BoardGameGeek"><span>-</span></div>';
2846    }
2847
2848    if (item.ratingUser > 0) {
2849        addRating += '<div class="game_rating r_second"><img src="/i/icn1.gif" width="24" height="29" alt="Средняя оценка" title="Средняя оценка"><span>' + item.ratingUser.toFixed(2) + '</span><sub>' + item.numVotes + '</sub></div>';
2850    } else {
2851        addRating += '<div class="game_rating r_second"><img src="/i/icn1.gif" width="24" height="29" alt="Средняя оценка" title="Средняя оценка"><span>-</span></div>';
2852    }
2853
2854    if (item.n10Rating > 0) {
2855        addRating += '<div class="game_rating r_third"><img src="/i/icn3.gif" width="24" height="29" alt="Рейтинг «Тесеры»" title="Рейтинг «Тесеры»"><span>' + item.n10Rating.toFixed(2) + '</span></div>';
2856    } else {
2857        addRating += '<div class="game_rating r_third"><img src="/i/icn3.gif" width="24" height="29" alt="Рейтинг «Тесеры»" title="Рейтинг «Тесеры»"><span>-</span></div>';
2858    }
2859
2860    var position = '<div class="game_position"><span>' + _this.counter + '</span></div>';
2861
2862    html += '<h2 class="games_title">' + position + '<a href="/game/' + item.alias + '" title="' + item.title + '" id="list-el-' + item.teseraId + '">' + item.title.substring(0, 40) + '</a>' + addRating + '</h2>';
2863    if (item.title2 !== undefined) {
2864        html += '<p class="games_date">' + item.title2 + (item.year > 0 ? ', ' + item.year : '' ) + '</p>';
2865    } else if (item.year > 0) {
2866        html += '<p class="games_date">' + item.year + '</p>';
2867    } else {
2868        html += '<p class="games_date">&nbsp;</p>';
2869    }
2870
2871    var detailsRow = '';
2872    if (item.playersMin || item.playersMax) {
2873        detailsRow += '<div><img src="/i/icons/ic1.gif" width="21" height="21" align="absmiddle" title="Число игроков"><span>';
2874        if (item.playersMin !== item.playersMax && item.playersMin > 0 && (item.playersMax === undefined || item.playersMax <= 0)) {
2875            detailsRow += 'от ' + item.playersMin;
2876        } else if (item.playersMin !== item.playersMax && item.playersMax > 0 && item.playersMin === undefined) {
2877            detailsRow += 'до ' + item.playersMax;
2878        } else if (item.playersMin === item.playersMax) {
2879            detailsRow += item.playersMin;
2880        } else {
2881            detailsRow += item.playersMin + ' - ' + item.playersMax;
2882        }
2883        detailsRow += '</span></div>';
2884    }
2885    if (item.playersMinRecommend || item.playersMaxRecommend) {
2886        detailsRow += '<div><img src="/i/icons/ic2.gif" width="21" height="21" align="absmiddle" title="Рекомендуемое число игроков"><span>';
2887        if (item.playersMinRecommend !== item.playersMaxRecommend && item.playersMinRecommend > 0 && (item.playersMaxRecommend === undefined || item.playersMaxRecommend <= 0)) {
2888            detailsRow += 'от ' + item.playersMinRecommend;
2889        } else if (item.playersMinRecommend !== item.playersMaxRecommend && item.playersMaxRecommend > 0 && item.playersMinRecommend === undefined) {
2890            detailsRow += 'до ' + item.playersMaxRecommend;
2891        } else if (item.playersMinRecommend === item.playersMaxRecommend) {
2892            detailsRow += item.playersMinRecommend;
2893        } else {
2894            detailsRow += item.playersMinRecommend + ' - ' + item.playersMaxRecommend;
2895        }
2896        detailsRow += '</span></div>';
2897    }
2898    if (item.playersAgeMin > 0) {
2899        detailsRow += '<div><img src="/i/icons/ic5.gif" width="21" height="21" align="absmiddle" title="Возраст"><span>от ' + item.playersAgeMin + ' лет</span></div>';
2900    }
2901    if (item.timeToLearn > 0) {
2902        detailsRow += '<div><img src="/i/icons/ic3.gif" width="21" height="21" align="absmiddle" title="Время освоения"><span>' + item.timeToLearn + ' мин</span></div>';
2903    }
2904    if (item.playtimeMin || item.playtimeMax) {
2905        detailsRow += '<div><img src="/i/icons/ic4.gif" width="21" height="21" align="absmiddle" title="Время партии"><span>';
2906        if (item.playtimeMin !== item.playtimeMax && item.playtimeMin > 0 && (item.playtimeMax === undefined || item.playtimeMax <= 0)) {
2907            detailsRow += 'от ' + item.playtimeMin + ' мин';
2908        } else if (item.playtimeMin !== item.playtimeMax && item.playtimeMax > 0 && item.playtimeMin === undefined) {
2909            detailsRow += 'до ' + item.playtimeMax + ' мин';
2910        } else if (item.playtimeMin === item.playtimeMax) {
2911            detailsRow += item.playtimeMin;
2912        } else {
2913            detailsRow += item.playtimeMin + ' - ' + item.playtimeMax + ' мин';
2914        }
2915        detailsRow += '</span></div>';
2916    }
2917
2918
2919    var logo = item.photoUrl === undefined ? 'https://tesera.ru/images/items/1175875,3/124x124xpa/photo1.' : item.photoUrl.replace('photo1.jpg', '200x200xca/photo1.jpg');
2920
2921    if (detailsRow === '') {
2922        detailsRow = '&nbsp;';
2923    }
2924
2925    html += '<table class="game_details_table" align="left" border="0" cellpadding="0" cellspacing="0" width="100%">' +
2926        '<tr><td class="game_details">' + detailsRow + '</td></tr></table>';
2927
2928    if (data.description === undefined) {
2929        data.description = '&hellip;';
2930    }
2931
2932    var controlHtml = '';
2933    if (data.subscribe !== undefined) {
2934        var objectType = data.objectType.toLowerCase();
2935        var alias = '';
2936        if (objectType === 'game') {
2937            alias = data.game.alias;
2938        }
2939
2940        if (data.subscribe.isSubscribe === false) {
2941            controlHtml += '<a href="#" data-type="1" onclick="subscribeObjectFeed(\'' + objectType + '\', \'' + alias + '\', this); return false;" class="is_subscribe" id="control-subs">Подписаться</a>';
2942        } else {
2943            controlHtml += '<a href="#" data-type="2" onclick="subscribeObjectFeed(\'' + objectType + '\', \'' + alias + '\', this); return false;" class="no_subscribe" id="control-subs">Отписаться</a>';
2944        }
2945    }
2946    if (data.collections !== undefined) {
2947        $.each(data.collections.collections, function (index, it) {
2948            if (it.type === 'ToPlay' && it.inCollection === true) {
2949                controlHtml += '<span id="cntrl-toplay-' + item.teseraId + '"><a href="#" data-type="1" onclick="delGameCollection(\'' + item.teseraId + '\', \'toplay\', false, function() { updateControl(\'' + item.teseraId  + '\', 2) }); return false;" class="no_subscribe">Хочу сыграть</a></span>';
2950            } else if (it.type === 'ToPlay' && it.inCollection === false) {
2951                controlHtml += '<span id="cntrl-toplay-' + item.teseraId + '"><a href="#" data-type="2" onclick="addGameToPlay(\'' + item.teseraId + '\'); return false;" class="is_subscribe">Хочу сыграть</a></span>';
2952            }
2953        });
2954
2955        $.each(data.collections.tradeCollections, function (index, it) {
2956            if (it.type === 'Purchases' && it.inCollection === true) {
2957                controlHtml += '<span id="cntrl-buy-' + item.teseraId + '"><a href="#" data-type="3" onclick="delAdv(' + it.item.id + ', \'purchases\', false, false, function() { updateControl(\'' + item.teseraId  + '\', 4, ' + it.item.id + ') }); return false;" class="no_subscribe">Хочу купить</a></span>';
2958            } else if (it.type === 'Purchases' && it.inCollection === false) {
2959                controlHtml += '<span id="cntrl-buy-' + item.teseraId + '"><a href="#" data-type="4" onclick="addGameBuy(\'' + item.teseraId + '\'); return false;" class="is_subscribe">Хочу купить</a></span>';
2960            }
2961        });
2962    }
2963
2964    if (userInfo.rightGroup != undefined && (checkRights(_this.addInfo.rights.create, userInfo.rightGroup.toLowerCase()) || userInfo.rightGroup.toLowerCase() === 'editor')) {
2965        controlHtml += '<a class="is_edit" href="/list_element1/' + data.itemTeseraId + '/">Редактировать</a>';
2966    }
2967
2968    html += '<div class="control_line_wrapper control_line_list_element">' + controlHtml + '</div>';
2969
2970    html += '<table align="left" border="0" cellpadding="0" cellspacing="0" width="100%"><tr>
2970' +
2971        '<td width="125px" height="125px" valign="top" class="book_pic"><img src="' + logo + '"/></td>' +
2972        '<td valign="top"><div class="feed_message">' + data.description.replace(/\n/g, "<br />"); + '</div></td>' +
2973'</tr>' +
2974'<tr>' +
2975'<td class="diary_second_line">';
2976
2977    html += '</td></tr><tr><td></td><td>';
2978
2979    html += '</td></tr></table>';
2980
2981    html += '<div id="' + data.itemTeseraId + '_comments" src="/list_element_comments_block/' + data.itemTeseraId + '/"></div>';
2982
2983    var tesId = data.itemTeseraId;
2984    $.ajax({
2985        type: "get",
2986        url: siteUrl + '/list_element_comments_block/' + data.itemTeseraId + '/',
2987        success: function (data) {
2988            $('#' + tesId + '_comments').html(data);
2989        }
2990    });
2991
2992    html += '</div>';
2993
2994    return html;
2995}
2996function elementListsItemDrawGrid(item, _this)
2997{
2998    var html = '<div class="c_game_item">';
2999    html += '<span class="list_item_grid_counter">' + _this.counter + '</span>';
3000
3001    var logo = item.game.photoUrl === undefined ? 'https://tesera.ru/images/items/1175875,3/124x124xpa/photo1.' : item.game.photoUrl;
3002    html += '<a class="c_game_img_wrap" href="/game/' + item.game.alias + '"><img class="c_game_img2" src="' + logo + '"/></a>';
3003
3004    var title = item.game.title.length > 34 ? item.game.title.substring(0, 30) + '&hellip;' : item.game.title;
3005    html += '<p class="m_game_title"><a title="' + item.game.title + '" href="/game/' + item.game.alias + '">' + title + '</a></p>';
3006
3007    html += '</div>';
3008
3009    return html;
3010}
3011function simpleCollectionsItemDrawList(item, _this)
3012{
3013    var html = '<li><a href="' + siteUrl + '/user/' + _this.userId + '/lists/' + item.id + '">' + item.title + '</a></li>';
3014
3015    return html;
3016}
3017function tblsimpleCollectionsItemDrawList(item, gameId, userId)
3018{
3019    var html = '';
3020
3021    $('#count-custom-lists').html(item.customCollections.length);
3022
3023    if (item.customCollections.length > 0) {
3024        $.each(item.customCollections, function(index, item2) {
3025            html += '<tr>' +
3026                '<td><a href="' + siteUrl + '/user/' + userId + '/lists/' + item2.id + '">' + item2.title + '</a></td>' +
3027                '<td style="text-align: right;"><input type="checkbox" name="list-' + item2.id + '" ' + (item2.inCollection ? 'checked="checked"' : '') + ' onchange=" $(this).is(\':checked\') === true ? addNewGameToCustomList(0, ' + item2.id + ') : delGameCustomList(' + item2.id + ', ' + gameId + ', undefined, true)" /></td>' +
3028                '</tr>';
3029        });
3030    } else {
3031        html += '<tr><td colspan="2">Нет записей</td></tr>';
3032    }
3033
3034    return html;
3035}
3036function fullCollectionsItemDrawList(item, _this)
3037{
3038    var html = '<div class="feed_item">';
3039
3040//	html += '<p class="diary_date">Создан: ' + myDate(item.creationDateUtc) + (myDate(item.creationDateUtc) !== myDate(item.modificationDateUtc) ? '; Обновлён: ' + myDate(item.modificationDateUtc) : '') + '</p>';
3041    html += '<p class="diary_date">' + myDate(item.creationDateUtc) + '</p>';
3042    html += '<h2 class="diary_title"><a href="' + siteUrl + '/user/' + _this.addInfo.userId + '/lists/' + item.id + '" id="el-' + item.id + '-title">' + item.title + '</a>';
3043
3044    if (item.isPrivate === true) {
3045        html += '<span id="el-' + item.id + '-private" val="1">приватный</span>';
3046    }
3047
3048    html += '<div class="diary_rating"><img src="/i/xc-w1.gif" width="31" height="27" alt="Количество игр в списке" title="Количество игр в списке">\n\
3049			<span>' + item.gamesTotal + '</span></div>';
3050
3051    html += '</h2>';
3052
3053    if (_this.addInfo.userId === _this.addInfo.authId) {
3054        html += '<div class="control_line_wrapper control_line_custom_list_element">' +
3055            '<a class="is_edit" href="#" onclick="openNewPopup(\'updCustomList\', ' + item.id + '); return false;">Редактировать</a>' +
3056            '<a class="is_del" href="#" onclick="delCustomList(' + item.id + ', function() { apiLists.init(undefined, true); }); return false;">Удалить</a>' +
3057            '</div>';
3058    }
3059
3060    if (item.description !== undefined) {
3061        html += '<div id="el-' + item.id + '-descr">' + item.description + '</div>';
3062    }
3063
3064    if (item.games.length > 0) {
3065        html += '<div class="custom_simple_games">';
3066        $.each(item.games, function(index, item2) {
3067            if (index < 5) {
3068                html += '<a class="custom_games" href="' + siteUrl + '/game/' + item2.game.teseraId + '">\n\
3069							<img src="' + item2.game.photoUrl + '" alt="' + item2.game.title + '" title="' + item2.game.title + '" />\n\
3070						</a>';
3071//				html += '<div class="feed_firstImg" style="background: url(' + item2.game.photoUrl + ') 0% 0% / 100px 100px no-repeat scroll content-box border-box transparent;"></div>';
3072            }
3073        });
3074
3075        if (item.gamesTotal > 5) {
3076            html += '<a href="' + siteUrl + '/user/' + _this.addInfo.userId + '/lists/' + item.id + '" class="add_custom_games">+' + (item.gamesTotal - 5) + '</a>';
3077        }
3078
3079        html += '</div>';
3080    }
3081
3082    html += '</div>';
3083
3084    return html;
3085}
3086function gameCollectionsItemDrawList(item, _this)
3087{
3088    var html = '<div class="feed_item">';
3089
3090    html += '<p class="feed_date">' + myDate(item.creationDateUtc) + '</p>';
3091
3092    html += '<table align="left" border="0" cellpadding="0" cellspacing="0" width="100%"><tr>';
3093
3094    if (item.game !== undefined && item.game.photoUrl !== undefined && item.game.photoUrl !== '') {
3095        html += '<td width="70px" valign="top">' +
3096            '<div class="feed_firstImg" style="background: url(' + item.game.photoUrl + ') 0% 0% / 70px 70px no-repeat scroll content-box border-box transparent;"></div>' +
3097            '</td>';
3098    } else {
3099        html += '<td width="70px">&nbsp;</td>';
3100    }
3101
3102    html += '<td valign="top">' +
3103        '<div class="feed_message"><b><a href="/game/' + item.game.teseraId + '">' + item.game.title + '</a></b>';
3104
3105    if (item.comment !== undefined) {
3106        html += '<span ><p class="feed_comment"><i>«' + item.comment + '»</i></p></span>';
3107    } else {
3108        html += '<span ></span>';
3109    }
3110
3111    html += '</div>' +
3112        '</td>';
3113
3114    if (_this.addInfo.userId === _this.addInfo.authId) {
3115        html += '<td width="110px" class="market_menu">';
3116
3117        html += '<a class="market_edit" href="#" onclick="openNewPopup(\'updGameToCustomList\', ' + item.game.teseraId + ', ' + _this.addInfo.listId + '); return false;"><img src="https://tesera.ru/i/x-edit.gif"/>редактировать</a>';
3118        html += '<a class="market_del" href="#" onclick="delGameCustomList(' + _this.addInfo.listId + ', ' + item.game.teseraId + ', function() { apiGames.init(undefined, true); } ); return false;"><img src="https://tesera.ru/i/x-delete.gif"/>удалить</a>';
3119        html += '</td>';
3120
3121
3122    } else {
3123        html += '<td width="70px">&nbsp;</td>';
3124    }
3125
3126    html += '</tr></table>';
3127
3128    html += '<input type="hidden" id="el-custom-' + item.game.teseraId + '-title" value="' + item.game.title + '"/>\n\
3129			<input type="hidden" id="el-custom-' + item.game.teseraId + '-descr" value="' + (item.comment !== undefined ? item.comment : '') + '"/>';
3130
3131    html += '</div>';
3132
3133    return html;
3134}
3135function gameCollectionsItemDrawGrid(item, _this)
3136{
3137    var html = '<div class="c_game_item" id="cg-' + item.game.teseraId + '">';
3138
3139    if (_this.addInfo.userId === _this.addInfo.authId) {
3140        html += '<img src="/i/x-delete.gif" onclick="delGameCustomList(' + _this.addInfo.listId + ', ' + item.game.teseraId + ', function() { apiGames.init(undefined, true); } ); return false;" class="c_game_del"/>';
3141        html += '<img src="/i/x-edit.gif" onclick="openNewPopup(\'updGameToCustomList\', ' + item.game.teseraId + ', ' + _this.addInfo.listId + '); return false;" class="c_game_upd"/>';
3142    }
3143
3144    var logo = item.game.photoUrl === undefined ? 'https://tesera.ru/images/items/1175875,3/124x124xpa/photo1.' : item.game.photoUrl;
3145    html += '<a class="c_game_img_wrap" href="/game/' + item.game.alias + '"><img class="c_game_img2" src="' + logo + '"/></a>';
3146
3147    var title = item.game.title.length > 34 ? item.game.title.substring(0, 30) + '&hellip;' : item.game.title;
3148    html += '<p class="c_game_title"><a title="' + item.game.title + '" href="/game/' + item.game.alias + '" id="cg-' + item.relationId + '-title">' + title + '</a></p>';
3149
3150    html += '<div class="c_game_rating" title="Рейтинг Тесеры"><img src="/i/up-r2.gif"><span>' + (item.game.n10Rating <= 0 ? '-' : item.game.n10Rating) + '</span></div>';
3151
3152    html += '<input type="hidden" id="el-custom-' + item.game.teseraId + '-title" value="' + item.game.title + '"/>\n\
3153			<input type="hidden" id="el-custom-' + item.game.teseraId + '-descr" value="' + item.comment + '"/>';
3154
3155//	if (item.game.ratingMy !== undefined && item.game.ratingMy > 0) {
3156//		html += '<div class="c_game_rating" title="Моя оценка"><img src="/i/up-r.gif"><span>' + item.game.ratingMy + '</span></div>';
3157//	}
3158
3159    html += '</div>';
3160
3161    return html;
3162}
3163
3164/* Base collections */
3165function fullCollectionsToplayItemDrawList(item, _this)
3166{
3167    var isMy = _this.addInfo.userId === _this.addInfo.authId;
3168    var html = '<div class="c_game_item ' + (!_this.pageView ? 'mb0' : '') + '" id="cg-' + item.relationId + '" data-cdate="' + item.creationDateUtc + '">';
3169
3170    if (isMy) {
3171        html += '<img src="/i/x-delete.gif" onclick="delGameCollection(' + item.game.teseraId + ', \'toplay\', true, function() { ' + _this.name + '.init(undefined, true, true); }); return false;" class="c_game_del"/>';
3172    }
3173
3174    var logo = item.game.photoUrl === undefined ? 'https://tesera.ru/images/items/1175875,3/124x124xpa/photo1.' : item.game.photoUrl;
3175    html += '<a class="c_game_img_wrap" href="/game/' + item.game.alias + '"><img class="c_game_img2" src="' + logo + '"/></a>';
3176
3177    var comment = '';
3178    if (item.comment !== undefined) {
3179        comment = '<span class="c_game_comment_icon" onclick="collectionGamePlayInfo(' + item.relationId + '); return false;">1</span>';
3180        comment += '<div id="cg-play-' + item.relationId + '-comment" style="display: none;">' + item.comment + '</div>'
3181    }
3182    var title = item.game.title.length > 34 ? item.game.title.substring(0, 30) + '&hellip;' : item.game.title;
3183    html += '<p class="c_game_title"><a title="' + item.game.title + '" href="/game/' + item.game.alias + '" id="cg-' + item.relationId + '-title">' + title + '</a> ' + comment + ' </p>';
3184
3185    if (_this.sort === 'rating') {
3186        html += '<div class="c_game_rating" title="Рейтинг Тесеры"><img src="/i/up-r2.gif"><span>' + (item.game.n10Rating <= 0 ? '-' : item.game.n10Rating) + '</span></div>';
3187    }
3188
3189    html += '</div>';
3190
3191    return html;
3192}
3193function simpleCollectionsToplayItemDrawList(item, _this)
3194{
3195    let logo = item.game.photoUrl === undefined ? 'https://tesera.ru/images/items/1175875,3/124x124xpa/photo1.' : item.game.photoUrl;
3196    let title = item.game.title.length > 34 ? item.game.title.substring(0, 30) + '&hellip;' : item.game.title;
3197    let html = '<li><a title="' + item.game.title + '" href="/game/' + item.game.alias + '"><div class="custom_logo"><img src="' + logo + '" height="16"/></div>' + title + '</a></li>';
3198
3199    return html;
3200}
3201function fullCollectionsOwnItemDrawList(item, _this)
3202{
3203    var isMy = _this.addInfo.userId === _this.addInfo.authId;
3204    var html = '<div class="c_game_item ' + (!_this.pageView ? 'mb0' : '') + '" id="cg-' + item.relationId + '" data-cdate="' + item.creationDateUtc + '" data-edate="' + item.eventDate + '">';
3205
3206    if (isMy) {
3207        html += '<img src="/i/x-delete.gif" onclick="delGameCollection(' + item.game.teseraId + ', \'own\', true, function() { ' + _this.name + '.init(undefined, true, true); }); return false;" class="c_game_del"/>';
3208        html += '<img src="/i/x-edit.gif" onclick="showPopup(this, \'user_rel_owner_game0\', ' + item.relationId + ', 0, \'' + _this.addInfo.userId + '_rel_owner_user_game\', false, \'' + _this.addInfo.userId + '_rel_owner_user_games\', {\'target_id\': ' + _this.addInfo.userId + '}); return false;" class="c_game_upd"/>';
3209    }
3210
3211    var logo = item.game.photoUrl === undefined ? 'https://tesera.ru/images/items/1175875,3/124x124xpa/photo1.' : item.game.photoUrl;
3212    html += '<a class="c_game_img_wrap" href="/game/' + item.game.alias + '"><img class="c_game_img2" src="' + logo + '"/></a>';
3213
3214    var comment = '';
3215    if (item.comment !== undefined) {
3216        comment = '<span class="c_game_comment_icon" onclick="collectionGameInfo(' + item.relationId + '); return false;">1</span>';
3217        comment += '<div id="cg-' + item.relationId + '-comment" style="display: none;">' + item.comment + '</div>'
3218    }
3219    var title = item.game.title.length > 34 ? item.game.title.substring(0, 30) + '&hellip;' : item.game.title;
3220    html += '<p class="c_game_title"><a title="' + item.game.title + '" href="/game/' + item.game.alias + '" id="cg-' + item.relationId + '-title">' + title + '</a> ' + comment + ' </p>';
3221
3222    if (_this.sort === 'rating') {
3223        html += '<div class="c_game_rating_2" title="Рейтинг Тесеры"><img src="/i/up-r2.gif"><span>' + (item.game.n10Rating <= 0 ? '-' : item.game.n10Rating) + '</span></div>';
3224    } else if (_this.sort === 'ratinggeekbgg') {
3225        html += '<div class="c_game_rating_2" title="Рейтинг BGG"><img src="/i/bgg_16.gif"><span>' + (item.game.bggGeekRating <= 0 ? '-' : item.game.bggGeekRating.toFixed(2)) + '</span></div>';
3226    }
3227
3228    if (item.game.ratingMy !== undefined && item.game.ratingMy > 0) {
3229        html += '<div class="c_game_rating" title="Моя оценка"><img src="/i/up-r.gif"><span>' + item.game.ratingMy + '</span></div>';
3230    }
3231
3232    html += '</div>';
3233
3234    return html;
3235}
3236function simpleCollectionsOwnItemDrawList(item, _this)
3237{
3238    return simpleCollectionsToplayItemDrawList(item, _this);
3239}
3240function fullCollectionsPlayedItemDrawList(item, _this)
3241{
3242    var isMy = _this.addInfo.userId === _this.addInfo.authId;
3243    var html = '<div class="c_game_item ' + (!_this.pageView ? 'mb0' : '') + '" id="cg-' + item.relationId + '" data-cdate="' + item.creationDateUtc + '" data-edate="' + item.eventDate + '">';
3244
3245    if (isMy) {
3246        html += '<img src="/i/x-delete.gif" onclick="delGameCollection(' + item.game.teseraId + ', \'played\', true, function() { ' + _this.name + '.init(undefined, true, true); }); return false;" class="c_game_del"/>';
3247    }
3248
3249    var logo = item.game.photoUrl === undefined ? 'https://tesera.ru/images/items/1175875,3/124x124xpa/photo1.' : item.game.photoUrl;
3250    html += '<a class="c_game_img_wrap" href="/game/' + item.game.alias + '"><img class="c_game_img2" src="' + logo + '"/></a>';
3251
3252    var comment = '';
3253    if (item.comment !== undefined) {
3254        comment = '<span class="c_game_comment_icon" onclick="collectionGameInfo(' + item.relationId + '); return false;">1</span>';
3255        comment += '<div id="cg-' + item.relationId + '-comment" style="display: none;">' + item.comment + '</div>'
3256    }
3257    var title = item.game.title.length > 34 ? item.game.title.substring(0, 30) + '&hellip;' : item.game.title;
3258    html += '<p class="c_game_title"><a title="' + item.game.title + '" href="/game/' + item.game.alias + '" id="cg-' + item.relationId + '-title">' + title + '</a> ' + comment + ' </p>';
3259
3260    if (_this.sort === 'rating') {
3261        html += '<div class="c_game_rating_2" title="Рейтинг Тесеры"><img src="/i/up-r2.gif"><span>' + (item.game.n10Rating <= 0 ? '-' : item.game.n10Rating) + '</span></div>';
3262    }
3263
3264    if (item.game.ratingMy !== undefined && item.game.ratingMy > 0) {
3265        html += '<div class="c_game_rating" title="Моя оценка"><img src="/i/up-r.gif"><span>' + item.game.ratingMy + '</span></div>';
3266    }
3267
3268    html += '</div>';
3269
3270    return html;
3271}
3272function simpleCollectionsPlayedItemDrawList(item, _this)
3273{
3274    return simpleCollectionsToplayItemDrawList(item, _this);
3275}
3276function resetCollectionFilters(alias)
3277{
3278    let name = 'apiList' + alias.charAt(0).toUpperCase() + alias.substr(1);
3279    window[name].filterSort('creationdateutc');
3280}
3281function simpleCollectionsTopItemDrawList(item, _this)
3282{
3283    let title = item.game.title.length > 34 ? item.game.title.substring(0, 30) + '&hellip;' : item.game.title;
3284    let html = '<li><a title="' + item.game.title + '" href="/game/' + item.game.alias + '" class="fix">' + title + '</a><span class="rating">' + item.game.ratingMy + '</span></li>';
3285
3286    return html;
3287}
3288function fullCollectionsTopItemDrawList(item, _this)
3289{
3290    let html = '<div class="c_game_item">';
3291
3292    let logo = item.game.photoUrl === undefined ? 'https://tesera.ru/images/items/1175875,3/124x124xpa/photo1.' : item.game.photoUrl;
3293    html += '<a class="c_game_img_wrap" href="/game/' + item.game.alias + '"><img class="c_game_img2" src="' + logo + '"/></a>';
3294
3295    let title = item.game.title.length > 34 ? item.game.title.substring(0, 30) + '&hellip;' : item.game.title;
3296    html += '<p class="c_game_title"><a title="' + item.game.title + '" href="/game/' + item.game.alias + '">' + title + '</a></p>';
3297
3298    html += '<div class="c_game_pos">' + _this.counter + '</div>';
3299    if (item.game.ratingMy !== undefined && item.game.ratingMy > 0) {
3300        html += '<div class="c_game_rating_top"><img src="/i/up-r.gif" align="absmiddle"><span>' + item.game.ratingMy + '</span></div>';
3301    }
3302
3303    html += '</div>';
3304
3305    return html;
3306}
3307
3308/* Trade collections */
3309function simpleTradeSalesItemDrawList(item, _this)
3310{
3311    let logo = item.game.photoUrl === undefined ? 'https://tesera.ru/images/items/1175875,3/124x124xpa/photo1.' : item.game.photoUrl;
3312    let title = item.game.title.length > 34 ? item.game.title.substring(0, 30) + '&hellip;' : item.game.title;
3313    let html = '<li><a title="' + item.game.title + '" href="/game/' + item.game.alias + '"><div class="custom_logo"><img src="' + logo + '" height="16"/></div>' + title + '</a>';
3314
3315    html += '<span class="right_info">';
3316
3317    let cond = '';
3318    if (item.condition === 'Used') {
3319        cond += 'б/у';
3320    } else if (item.condition === 'New') {
3321        cond += 'новая';
3322    }
3323
3324    if (cond !== '' && item.price !== undefined) {
3325        html += cond + ', ';
3326    }
3327
3328    if (item.price !== undefined) {
3329        html += '' + item.price + ' руб.';
3330    }
3331
3332    html += '</li>';
3333
3334    return html;
3335}
3336function simpleTradePurchasesItemDrawList(item, _this)
3337{
3338    let logo = item.game.photoUrl === undefined ? 'https://tesera.ru/images/items/1175875,3/124x124xpa/photo1.' : item.game.photoUrl;
3339    let title = item.game.title.length > 34 ? item.game.title.substring(0, 30) + '&hellip;' : item.game.title;
3340    let html = '<li><a title="' + item.game.title + '" href="/game/' + item.game.alias + '"><div class="custom_logo"><img src="' + logo + '" height="16"/></div>' + title + '</a>';
3341
3342    html += '<span class="right_info">';
3343
3344    let cond = '';
3345    if (item.condition === 'Used') {
3346        cond += 'б/у';
3347    } else if (item.condition === 'New') {
3348        cond += 'новая';
3349    }
3350
3351    if (cond !== '' && item.price !== undefined) {
3352        html += cond + ', ';
3353    }
3354
3355    if (item.price !== undefined) {
3356        html += '' + item.price + ' руб.';
3357    }
3358
3359    html += '</li>';
3360
3361    return html;
3362}
3363
3364/* Custom lists */
3365function newCustomList(id)
3366{
3367    var html = '<h2>Создание списка</h2>' +
3368        '<img class="close" src="/img/p_close.gif" border="0" onclick="closePopup(\'' + id + '\')"><br>' +
3369        '<form id="rel" method="post" action="" enctype="multipart/form-data">' +
3370        '<input type="hidden" name="id" value="0">' +
3371        '<input type="hidden" id="linked_id" value="">' +
3372        '<table cellpadding="0" cellspacing="0" border="0" class="form">' +
3373        '<tbody><tr><td colspan="2"><hr></td></tr>' +
3374        '<tr><td width="160"><h3>название</h3></td>' +
3375        '<td>' +
3376        '<input type="text" name="rel_title" style="width:100%;" />' +
3377        '</td>' +
3378        '</tr><tr><td colspan="2">&nbsp;</td></tr>' +
3379        '<tr><td width="160" valign="top"><h3>описание</h3></td>' +
3380        '<td>' +
3381        '<textarea name="rel_comments" style="width:100%; height: 130px"></textarea>' +
3382        '</td>' +
3383        '</tr>' +
3384        '<tr><td colspan="2">&nbsp;</td></tr>' +
3385        '<tr>
3385<td width="160" valign="top"><h3>приватный?</h3></td>' +
3386        '<td>' +
3387        '<input style="float: left; margin-right: 10px" type="radio" name="rel_is_priv" value="0" checked="checked" id="rel_is_priv_0"><h3 style="float: left; margin-right: 20px"><label for="rel_is_priv_0">нет</label></h3>' +
3388        '<input style="float: left; margin-right: 10px" type="radio" name="rel_is_priv" value="1" id="rel_is_priv_1"><h3 style="float: left; margin-right: 20px"><label for="rel_is_priv_1">да</label></h3></td></tr>' +
3389        '<tr><td colspan="2">' +
3390        '<div id="ajx_loader" class="ajx_loader"></div>' +
3391        '</td></tr>' +
3392        '<tr><tr><td colspan="2"><hr></td></tr><td colspan="2">' +
3393        '<div form="rel" class="submit save green create" onclick="addNewCustomList(0, function() { apiLists.init(undefined, true); closePopup(\'' + id + '\'); })"><span>создать</span></div>' +
3394        '</td></tr>' +
3395        '</tbody>' +
3396        '</table>' +
3397        '</form>';
3398
3399    return html;
3400}
3401function updCustomList(id, args)
3402{
3403    var alias = args[0];
3404    var cName = args[1];
3405
3406    var descr = $('#el-' + alias + '-descr').html() != undefined ? $('#el-' + alias + '-descr').html() : '';
3407    var isPrivate = $('#el-' + alias + '-private').attr('val') == 1 ? true : false;
3408
3409    var html = '<h2>Редактирование списка</h2>' +
3410        '<img class="close" src="/img/p_close.gif" border="0" onclick="closePopup(\'' + id + '\')"><br>' +
3411        '<form id="rel" method="post" action="" enctype="multipart/form-data">' +
3412        '<input type="hidden" name="id" value="0">' +
3413        '<input type="hidden" id="linked_id" value="">' +
3414        '<table cellpadding="0" cellspacing="0" border="0" class="form">' +
3415        '<tbody><tr><td colspan="2"><hr></td></tr>' +
3416        '<tr><td width="160"><h3>название</h3></td>' +
3417        '<td>' +
3418        '<input type="text" name="rel_title" style="width:100%;" value="' + $('#el-' + alias + '-title').html() + '" />' +
3419        '</td>' +
3420        '</tr><tr><td colspan="2">&nbsp;</td></tr>' +
3421        '<tr><td width="160" valign="top"><h3>описание</h3></td>' +
3422        '<td>' +
3423        '<textarea name="rel_comments" style="width:100%; height: 130px">' + descr + '</textarea>' +
3424        '</td>' +
3425        '</tr>' +
3426        '<tr><td colspan="2">&nbsp;</td></tr>' +
3427        '<tr><td width="160" valign="top"><h3>приватный?</h3></td>' +
3428        '<td>' +
3429        '<input style="float: left; margin-right: 10px" type="radio" name="rel_is_priv" value="0" ' + (!isPrivate ? 'checked="checked"' : '') + ' id="rel_is_priv_0"><h3 style="float: left; margin-right: 20px"><label for="rel_is_priv_0">нет</label></h3>' +
3430        '<input style="float: left; margin-right: 10px" type="radio" name="rel_is_priv" value="1" ' + (isPrivate ? 'checked="checked"' : '') + ' id="rel_is_priv_1"><h3 style="float: left; margin-right: 20px"><label for="rel_is_priv_1">да</label></h3></td></tr>' +
3431        '<tr><td colspan="2">' +
3432        '<div id="ajx_loader" class="ajx_loader"></div>' +
3433        '</td></tr>' +
3434        '<tr><tr><td colspan="2"><hr></td></tr><td colspan="2">' +
3435        '<div form="rel" class="submit save green create" onclick="addNewCustomList(' + alias + ', function() { ' + (cName !== undefined ? 'apiLists.init(function(data) {' + cName + '(data)}, true);' : 'apiLists.init(undefined, true);') + ' closePopup(\'' + id + '\'); })"><span>обновить</span></div>' +
3436        '</td></tr>' +
3437        '</tbody>' +
3438        '</table>' +
3439        '</form>';
3440
3441    return html;
3442}
3443function addNewCustomList(id, callback)
3444{
3445    var title = $('input[name="rel_title"]').val();
3446    var comment = $('textarea[name="rel_comments"]').val();
3447    var isprivate = $('input[name="rel_is_priv"]:checked').val();
3448
3449    if (title.length <= 3) {
3450        alert('Название списка должно состоять как минимум из 4 символов!');
3451        return false;
3452    }
3453
3454    if (id > 0) {
3455        $.ajax({
3456            type: "put",
3457            dataType: 'json',
3458            url: apiHost + '/collections/custom/' + id,
3459            contentType: "application/json",
3460            data: JSON.stringify({
3461                title: title,
3462                description: comment,
3463                IsPrivate: isprivate == 1 ? true : false
3464            }),
3465            beforeSend: function (xhr) {
3466                xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
3467            },
3468            complete: function () {
3469                callback();
3470            }
3471        });
3472    } else {
3473        $.ajax({
3474            type: "post",
3475            dataType: 'json',
3476            url: apiHost + '/collections/custom',
3477            contentType: "application/json",
3478            data: JSON.stringify({
3479                title: title,
3480                description: comment,
3481                IsPrivate: isprivate == 1 ? true : false
3482            }),
3483            beforeSend: function (xhr) {
3484                xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
3485            },
3486            complete: function () {
3487                callback();
3488            }
3489        });
3490    }
3491}
3492function delCustomList(id, callback)
3493{
3494    if (!confirm('Удалить список и все записи в нём?')) {
3495        return false;
3496    }
3497
3498    $.ajax({
3499        type: "delete",
3500        dataType: 'json',
3501        url: apiHost + '/collections/custom/' + id,
3502        contentType: "application/json",
3503        beforeSend: function (xhr) {
3504            xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
3505        },
3506        complete: function () {
3507            callback();
3508        }
3509    });
3510}
3511function delGameCustomList(listId, gameId, callback, hard)
3512{
3513    if (hard !== true) {
3514        if (!confirm('Удалить игру из списка?')) {
3515            return false;
3516        }
3517    }
3518
3519    $.ajax({
3520        type: "delete",
3521        dataType: 'json',
3522        url: apiHost + '/collections/custom/' + listId + '/' + gameId,
3523        contentType: "application/json",
3524        beforeSend: function (xhr) {
3525            xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
3526        },
3527        complete: function () {
3528            if (callback !== undefined) {
3529                callback();
3530            }
3531        }
3532    });
3533}
3534function newGameToCustomList(id, listId)
3535{
3536    var html = '<h2>Добавить игру в список</h2>' +
3537        '<img class="close" src="/img/p_close.gif" border="0" onclick="closePopup(\'' + id + '\')"><br>';
3538
3539    html += '<form id="rel" method="post" action="/user_rel_played_game0/" enctype="multipart/form-data">'+
3540        '<input type="hidden" id="linked_id" name="target_id" value="0">' +
3541        '<table cellpadding="0" cellspacing="0" border="0" class="form">' +
3542        '<tbody><tr>' +
3543        '<td width="160"><h3>игра</h3></td>' +
3544        '<td>' +
3545        '<input type="text" id="rel_target_title" name="rel_target_title" maxlength="255" style="width:100%" value="" autocomplete="off">' +
3546        '</td>' +
3547        '</tr>' +
3548        '<tr><td colSpan="2">&nbsp;</td></tr>' +
3549        '<tr id="tr_comments">' +
3550        '<td width="160" valign="top"><h3>комментарий</h3></td>' +
3551        '<td>' +
3552        '<textarea id="rel_comments" name="rel_comments" style="width:100%; height: 130px"></textarea>' +
3553        '</td>' +
3554        '</tr>' +
3555        '<tr><td colspan="2">&nbsp;</td></tr>' +
3556        '<tr><td colspan="2"><hr></td></tr>' +
3557        '<tr><td colspan="2">' +
3558        '<div id="ajx_loader" class="ajx_loader"></div>' +
3559        '</td></tr>' +
3560        '<tr><td colspan="2">' +
3561        '<div form="rel" class="submit save green create" onclick="addNewGameToCustomList(0, ' + listId + ',  function() { apiGames.init(undefined, true); closePopup(\'' + id + '\'); })"><span class="">добавить</span></div>' +
3562        '</td></tr>' +
3563        '</tbody></table>' +
3564        '</form>';
3565
3566    setTimeout(function() {
3567        $('#rel_target_title').suggest('/search_items/9/3/', {'scheme': 'popup_title'});
3568    }, 500);
3569
3570    return html;
3571}
3572function updGameToCustomList(id, args)
3573{
3574    var gameId = args[0];
3575    var listId = args[1];
3576
3577    var html = '<h5><a href="' + siteUrl + '/game/' + gameId + '">' + $('#el-custom-' + gameId + '-title').val() + '</a></h5><h2>Обновить элемент списка</h2>' +
3578        '<img class="close" src="/img/p_close.gif" border="0" onclick="closePopup(\'' + id + '\')"><br>';
3579
3580    html += '<form id="rel" method="post" action="/user_rel_played_game0/" enctype="multipart/form-data">'+
3581        '<input type="hidden" id="linked_id" name="target_id" value="' + gameId + '">' +
3582        '<table cellpadding="0" cellspacing="0" border="0" class="form">' +
3583        '<tbody>' +
3584        '<tr id="tr_comments">' +
3585        '<td width="160" valign="top"><h3>комментарий</h3></td>' +
3586        '<td>' +
3587        '<textarea id="rel_comments" name="rel_comments" style="width:100%; height: 130px">' + $('#el-custom-' + gameId + '-descr').val() + '</textarea>' +
3588        '</td>' +
3589        '</tr>' +
3590        '<tr><td colspan="2">&nbsp;</td></tr>' +
3591        '<tr><td colspan="2"><hr></td></tr>' +
3592        '<tr><td colspan="2">' +
3593        '<div id="ajx_loader" class="ajx_loader"></div>' +
3594        '</td></tr>' +
3595        '<tr><td colspan="2">' +
3596        '<div form="rel" class="submit save green create" onclick="addNewGameToCustomList(' + gameId + ', ' + listId + ',  function() { apiGames.init(undefined, true); closePopup(\'' + id + '\'); })"><span class="">обновить</span></div>' +
3597        '</td></tr>' +
3598        '</tbody></table>' +
3599        '</form>';
3600
3601    return html;
3602}
3603function addNewGameToCustomList(id, listId, callback)
3604{
3605    var gameId = $('#linked_id').val();
3606    var comment = $('textarea[name="rel_comments"]').val() != undefined ? $('textarea[name="rel_comments"]').val() : '';
3607
3608    if (id > 0) {
3609        $.ajax({
3610            type: "put",
3611            dataType: 'json',
3612            url: apiHost + '/collections/custom/' + listId + '/' + id,
3613            contentType: "application/json",
3614            data: JSON.stringify({
3615                alias: gameId,
3616                comment: comment
3617            }),
3618            beforeSend: function (xhr) {
3619                xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
3620            },
3621            complete: function () {
3622                callback();
3623            }
3624        });
3625    } else {
3626        $.ajax({
3627            type: "post",
3628            dataType: 'json',
3629            url: apiHost + '/collections/custom/' + listId,
3630            contentType: "application/json",
3631            data: JSON.stringify({
3632                alias: gameId,
3633                comment: comment
3634            }),
3635            beforeSend: function (xhr) {
3636                xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
3637            },
3638            complete: function () {
3639                if (callback !== undefined) {
3640                    callback();
3641                }
3642            }
3643        });
3644    }
3645}
3646
3647
3648/*******************************************************************************
3649 * New core api
3650 */
3651class apiConfig {
3652
3653    constructor(name, object, type, limit, limit2) {
3654        this.name = name; // object var name
3655        this.limit = limit !== undefined ? limit : 0;
3656        this.limit2 = limit2 !== undefined ? limit2 : this.limit;
3657        this.offset = 0;
3658        this.sort = '';
3659        this.view = 'full'; // html view format
3660        this.object = object; // object for api query
3661        this.insertNew = true; // clear html and insert new, or append for isset html
3662        this.type = type !== undefined ? type : ''; // add info
3663        this.id = this.object;
3664        this.query = ''; // add query path, after general url
3665        this.htmlView = 'list'; // prefix for draw method
3666        this.counter = 0; // counter each record from draw method
3667        this.userId = null;
3668        this.notDraw = false;
3669        this.addInfo = {};
3670        this.emptyText = 'Записи не найдены';
3671        this.category = '';
3672        this.pageView = true;
3673        this.urlParams = {};
3674    }
3675
3676    init(callback, reload, clear) {
3677
3678        if (clear === true) {
3679            this.clearBeforeStart();
3680        }
3681
3682        if (reload === true) {
3683            $('#' + this.id).html('<div class="loader" id="loader"><img src="/i/loader.gif"></div>');
3684        }
3685
3686        this.viewToggle();
3687        this.startLoad(function (data) {
3688            if (callback !== undefined) {
3689                callback(data);
3690            }
3691        });
3692    }
3693
3694    startLoad(callback) {
3695        var _this  = this;
3696        this.loadApiData(function(data) {
3697            if (_this.notDraw === false) {
3698                _this.drawApiData(data);
3699            }
3700
3701            if (callback !== undefined) {
3702                callback(data);
3703            }
3704        });
3705    }
3706
3707    loadApiData(callback) {
3708        var html = '';
3709        var next = '';
3710        var limit = this.limit;
3711        var _this  = this;
3712
3713        if (this.htmlView === 'grid' && this.limit2 !== undefined) {
3714            limit = this.limit2;
3715        }
3716
3717        let strAddParams = '';
3718        $.each(_this.urlParams, function(key, value) {
3719            strAddParams += '&' + key + '=' + value;
3720        });
3721
3722        $.ajax({
3723            type: "get",
3724            dataType: 'json',
3725            url: apiHost + '/' + this.object + this.query + '?v=1'
3726                + (this.offset > 0 ? '&offset=' + this.offset : '')
3727                + (limit > 0 ? '&limit=' + limit : '')
3728                + (this.type !== '' ? '&type=' + this.type : '')
3729                + (this.sort !== '' ? '&sort=' + this.sort : '')
3730                + strAddParams,
3731            beforeSend: function (xhr) {
3732                xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
3733            },
3734            success: function (data) {
3735                if (_this.notDraw !== false) {
3736                    callback(data);
3737                    return;
3738                }
3739
3740                $.each(data, function(index, item) {
3741                    html += _this.itemDraw(item);
3742                });
3743            },
3744            complete: function (jqXHR) {
3745                if (_this.notDraw === false) {
3746                    var offset = jqXHR.getResponseHeader('x-offset');
3747                    var totalPages = jqXHR.getResponseHeader('x-total-pages');
3748                    var totalRecord = parseInt(jqXHR.getResponseHeader('x-total-count'));
3749                    var limit = parseInt(jqXHR.getResponseHeader('x-limit'));
3750                    var current = (parseInt(offset) + 2) * limit;
3751                    if (current > totalRecord) {
3752                        current = totalRecord;
3753                    }
3754
3755                    if (parseInt(offset) + 1 < totalPages) {
3756                        _this.offset = parseInt(offset) + 1;
3757                        next += '<a onclick="pageNextLoad(\'' + _this.name + '\'); return false;">Загрузить ещё ' + limit + (limit !== current ? ' (' + current + ') ' : ' ') + 'из ' + totalRecord + '</a>';
3758                    }
3759
3760                    if (html === '') {
3761                        html = '<p class="market_empty">' + _this.emptyText + '</p>';
3762                    }
3763
3764                    callback({
3765                        'html': html,
3766                        'total': jqXHR.getResponseHeader('x-total-count'),
3767                        'next': next
3768                    });
3769                }
3770            }
3771        });
3772    }
3773
3774    drawApiData(data) {
3775        $('#' + this.id + ' #loader').remove();
3776
3777        if (this.insertNew === true) {
3778            $('#' + this.id).html(data.html);
3779        } else {
3780            $('#' + this.id).append(data.html);
3781        }
3782
3783        this.insertNew = false;
3784
3785        $('#' + this.id + '-count').html(data.total);
3786
3787        if (this.pageView) {
3788            $('#' + this.id + '-pages').html(data.next);
3789        }
3790    }
3791
3792    itemDraw(item) {
3793        let object = this.object.charAt(0).toUpperCase() + this.object.substr(1);
3794        let view = this.htmlView.charAt(0).toUpperCase() + this.htmlView.substr(1);
3795        let category = this.category !== '' ? this.category.charAt(0).toUpperCase() + this.category.substr(1) : '';
3796        this.counter = this.counter + 1;
3797
3798        console.log('draw => ' + this.view + object + category + 'ItemDraw' + view + '()');
3799
3800        return executeFunctionByName(this.view + object + category + 'ItemDraw' + view, window, item, this);
3801    }
3802
3803    clearBeforeStart() {
3804        this.offset = 0;
3805        this.insertNew = true;
3806        this.counter = 0;
3807    }
3808
3809    filterSort(value) {
3810        this.clearBeforeStart();
3811        this.sort = value;
3812
3813        this.startLoad();
3814    }
3815
3816    pageNextLoad() {
3817        this.startLoad();
3818    }
3819
3820    viewToggle() {
3821        var view = localStorage.getItem(this.id);
3822        if (view === undefined || view === null || view === 'null') {
3823            this.htmlView = 'list';
3824        } else {
3825            this.htmlView = view;
3826        }
3827
3828        if (this.htmlView === 'list') {
3829            $('#' + this.id + '-list').addClass('active');
3830        } else {
3831            $('#' + this.id + '-grid').addClass('active');
3832        }
3833
3834        var _this  = this;
3835        $('#' + this.id + '-list').unbind('click').click(function () {
3836            $('#' + _this.id + '-list').addClass('active');
3837            $('#' + _this.id + '-grid').removeClass('active');
3838
3839            localStorage.setItem(_this.id, 'list');
3840            _this.htmlView = 'list';
3841
3842            _this.clearBeforeStart();
3843            _this.startLoad();
3844        });
3845
3846        $('#' + this.id + '-grid').unbind('click').click(function () {
3847            $('#' + _this.id + '-grid').addClass('active');
3848            $('#' + _this.id + '-list').removeClass('active');
3849
3850            localStorage.setItem(_this.id, 'grid');
3851            _this.htmlView = 'grid';
3852
3853            _this.clearBeforeStart();
3854            _this.startLoad();
3855        });
3856    }
3857};
3858
3859function pageNextLoad(name)
3860{
3861    eval(name).pageNextLoad();
3862}
3863
3864function executeFunctionByName(functionName, context /*, args */)
3865{
3866    var args = Array.prototype.slice.call(arguments, 2);
3867    var namespaces = functionName.split(".");
3868    var func = namespaces.pop();
3869
3870    for(var i = 0; i < namespaces.length; i++) {
3871        context = context[namespaces[i]];
3872    }
3873
3874    return context[func].apply(context, args);
3875}
3876
3877let userInfo = {};
3878
3879function checkRights(require, user)
3880{
3881    if (require === 'user' && ['user', 'trusted', 'editor', 'admin'].includes(user)) {
3882        return true;
3883    } else if (require === 'trusted' && ['trusted', 'editor', 'admin'].includes(user)) {
3884        return true;
3885    } else if (require === 'editor' && ['editor', 'admin'].includes(user)) {
3886        return true;
3887    } else if (require === 'admin' && ['admin'].includes(user)) {
3888        return true;
3889    } else {
3890        return false;
3891    }
3892}
3893
3894function openNewPopup(id /*, args */)
3895{
3896    var html = '<div class="popuper" style="z-index: 10000; margin-left: -284px; margin-top: -185px; display: block;" id="' + id + '">' +
3897        '<div class="popup-top">&nbsp;</div>' +
3898        '<div class="popup">';
3899
3900    var args = Array.prototype.slice.call(arguments, 1);
3901    html += executeFunctionByName(id, window, id, args);
3902
3903    html +=			'</div>' +
3904        '<div class="popup-bottom">&nbsp;</div>' +
3905        '</div>';
3906
3907    $('#popup-area').html(html);
3908}
3909
3910function linkify(inputText) {
3911    var replacedText, replacePattern1, replacePattern2, replacePattern3;
3912
3913    //URLs starting with http://, https://, or ftp://
3914    replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
3915    replacedText = inputText.replace(replacePattern1, '<a href="$1" target="_blank">$1</a>');
3916
3917    //URLs starting with "www." (without // before it, or it'd re-link the ones done above).
3918    replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
3919    replacedText = replacedText.replace(replacePattern2, '$1<a href="http://$2" target="_blank">$2</a>');
3920
3921    //Change email addresses to mailto:: links.
3922    replacePattern3 = /(([a-zA-Z0-9\-\_\.])+@[a-zA-Z\_]+?(\.[a-zA-Z]{2,6})+)/gim;
3923    replacedText = replacedText.replace(replacePattern3, '<a href="mailto:$1">$1</a>');
3924
3925    return replacedText;
3926}
3927
3928/**********************************************
3929 * MAP
3930 */
3931
3932/**
3933 *
3934 * @param {type} extUserId
3935 * @param {type} callback
3936 * @returns {undefined}
3937 */
3938function loadSettings(extUserId, callback)
3939{
3940    let addQuery = (extUserId !== undefined && extUserId !== null && extUserId !== '') ? '/user/' + extUserId : '';
3941
3942    $.ajax({
3943        type: "get",
3944        dataType: 'json',
3945        url: apiHost + '/map' + addQuery,
3946        beforeSend: function (xhr) {
3947            xhr.setRequestHeader('Authorization', 'Bearer ' + bearerToken);
3948        },
3949        success: function (data) {
3950            return callback(data);
3951        },
3952        error: function () {
3953            let data = {mapUser: undefined};
3954            return callback(data);
3955        }
3956    });
3957}
3958
3959
3960/* Users similar */
3961function fullUserSimilarItemDrawList(item, _this)
3962{
3963    let html = '<div class="feed_item">';
3964
3965    let user = item.similarUser
3966    let author = user.name;
3967    if (user.name.toString().toLowerCase() !== user.login.toString().toLowerCase()) {
3968        author += ' (' + user.login + ')';
3969    }
3970
3971    let userLine = '<h2 class="similar_title"><a href="/user/' + user.teseraId + '/games/top/">' + author + '</a></h2>';
3972
3973    let ava = user.avatarUrl === undefined ? 'https://tesera.ru/images/items/1366656,22/125x125/photo.' : user.avatarUrl;
3974    html += '<table align="left" border="0" cellpadding="0" cellspacing="0" width="100%">' +
3975        '<tr>' +
3976        '<td width="100px" valign="top">' +
3977        '<div class="similar_position"><span>' + _this.counter + '</span></div>' +
3978        '<div class="u_avatar" style="background: url(' + ava + ') 0% 0% / 100px 100px no-repeat scroll content-box border-box transparent;"></div></td>';
3979
3980    html += '<td valign="top">' + userLine;
3981    html += '<div class="bold_line">';
3982    html += '<div class="u_user_general_info"><img src="/i/own.png" width="25" height="15" title="Всего игр в коллекции"><span>' + item.gamesInCollection + '</span></div>';
3983    html += '<div class="u_user_general_info"><img src="/i/list-element.svg?123" height="25" title="Всего пересечений (+-1 балл)"><span>' + item.commonGames + ' (' + item.rateOnePoint + ')</span></div>';
3984    html += '<div class="u_user_general_info"><img src="/i/games.svg?123" height="25" title="Коэффициент схожести"><span>' + item.weight + '</span></div>';
3985    html += '</div>';
3986    html += '</td>';
3987
3988    html += '</tr></table></div>';
3989
3990    return html;
3991}
3992
3993function simpleUserSimilarItemDrawList(item, _this)
3994{
3995    let user = item.similarUser
3996
3997    let ava = user.avatarUrl === undefined ? 'https://tesera.ru/images/items/1366656,22/125x125/photo.' : user.avatarUrl;
3998
3999    let html = '<tr>' +
4000        '<td class="ava"><a href="/user/' + user.teseraId + '/games/top/"><img class="pic" src="' + ava + '" width="24" height="24" alt="" title=""></a></td>' +
4001        '<td class="nick"><a href="/user/' + user.teseraId + '/games/top/">' + user.login + '</a></td>' +
4002        '<td class="similar-rate">+' + item.weight + ' <span title="(пересечений +-1 балл)">(' + item.rateOnePoint + ')</span></td>' +
4003        '</tr>';
4004
4005    return html;
4006}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.