Skip to content

Instantly share code, notes, and snippets.

@andersevenrud
Last active November 30, 2020 01:44
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andersevenrud/c684dac238b87fdc8bda to your computer and use it in GitHub Desktop.
Save andersevenrud/c684dac238b87fdc8bda to your computer and use it in GitHub Desktop.
Fullscreen Battlelog Server Browser (BF4)
// ==UserScript==
// @name Fullscreen Battlelog Server Browser (BF4)
// @namespace http://andersevenrud.github.io/
// @version 0.5.0
// @description Fullscreen Battlelog Server Browser (BF4)
// @match http://battlelog.battlefield.com/bf4
// @match http://battlelog.battlefield.com/bf4/*
// @match http://cte.battlelog.battlefield.com/bf4
// @match http://cte.battlelog.battlefield.com/bf4/*
// @match http://cte.battlelog.com/bf4
// @match http://cte.battlelog.com/bf4/*
// @author andersevenrud
// @updateURL https://gist.github.com/andersevenrud/c684dac238b87fdc8bda/raw/bl-fullscreen-server-browser.user.js
// @downloadURL https://gist.github.com/andersevenrud/c684dac238b87fdc8bda/raw/bl-fullscreen-server-browser.user.js
// ==/UserScript==
/*
=== ABOUT ===
NOTE: If you downloaded v0.1 you have to re-install this script to make auto-update work. Sorry about that :(
This is a fullscreen server browser for Battlefield 4 in Battlelog. It works just like the normal one only it
shows all servers (with the filter you have set) without scrolling and is available from any page with the
click of one button. A bit more convenient if you ask me :)
=== TROUBLESHOOTING ===
If you get no results, first try to click "refresh" button. If nothing still comes up, go to your filters
and click "Apply Filter". Thanks /u/estrangeiro14 reporting and solving this issue :)
=== TODO ===
- Clean up code when done
- Nicer UI
- Add filter/form from normal browser ?!
- Support for adding notes on spesific servers
- Support for favourite list viewing and not just browsing
- BBLog integration ?
=== INSTALLATION INSTRUCTIONS ===
- Chrome: Install Tampermonkey extension
- Opera: Install Tampermonkey extension
- Firefox: Install Greasemonkey extension
Then simply click the "Raw" button in this gist and it will install and auto-update by itself :)
=== SCREENSHOTS ===
These might be a bit outdated by now
- Button: http://i.imgur.com/pTFBzbQ.jpg
- Fullscreen view: http://i.imgur.com/wuNjEdg.png
- Detail view: http://i.imgur.com/fDvpSg3.png
=== ALSO TAKE A LOOK AT ===
* Bring Battlelog Hooahs back!
https://gist.github.com/andersevenrud/c4cf8ec40ed25c2ef2cf
* Better Battlelog Loadout
https://gist.github.com/andersevenrud/d9f3ae140a587106f21d
=== LINKS ===
Reddit thread: https://www.reddit.com/r/battlefield_4/comments/3jzk0r/battlelog_fullscreen_server_browser_script/
=== CHANGELOG ===
0.1 - Initial release
0.2 - Fixed sorting of table in some circumstances. Possible fix for injection not working properly
0.3 - Made some of the color prettier
0.3.1 - Fix for y-scroll on close
0.3.2 - Removed duplicate entries. Pinging bugfixes
0.3.3 - Fix sorting on ping column
0.3.4 - You now see free slots when hovering over "Players" column (tooltip)
0.3.5 - Added link to full server page in table entries
0.3.6 - Adjusted position of close button
0.3.7 - Now works on frontpage (derp)
0.3.8 - Added sort by Name/Map/Mode in table
0.3.9 - Fixed so detail view dissapears when you sort a column
0.4.0 - Row now updates when you click on it (ping, player count etc)
0.4.1 - Clicked row scrolls into view
0.4.2 - Fixed detail view getting stuck
0.4.3 - Battlelog changed something so my listing broke. Fixed!
0.4.4 - Added loading text to header
0.4.5 - Added text when no results are found
0.4.9 - Added CTE support
0.5.0 - Added Tickrate column
*/
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// STYLES
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
var stylesheet = " \
#server-browser-overlay { \
position: fixed; \
top: 0px; \
left: 0px; \
bottom: 0px; \
right: 0px; \
background-color: rgba(0, 0, 0, .95); \
z-index: 9999999; \
padding: 40px; \
box-sizing: border-box; \
} \
#server-browser-overlay > h1 { \
position: relative; \
} \
#server-browser-overlay > h1 > span.refresh { \
cursor: pointer; \
padding-left : 40px; \
} \
#server-browser-list { \
position : absolute; \
top : 120px; \
left : 40px; \
right : 40px; \
bottom : 40px; \
overflow : auto; \
} \
#server-browser-list table {\
width : 100%; \
font-size : 14px; \
} \
#server-browser-list th, \
#server-browser-list td {\
padding : 10px; \
vertical-align : top; \
} \
#server-browser-list tr {\
} \
#server-browser-list .column-mode, \
#server-browser-list .column-map {\
display : none; \
} \
#server-browser-list tbody tr.server-entry:hover {\
background-color : #333 !important; \
} \
#server-browser-list tbody tr.active {\
background-color : #333 !important; \
} \
#server-browser-list td .server-name {\
display : block; \
position : relative; \
} \
#server-browser-list td .server-name .icon {\
margin-left : 10px; \
vertical-align : middle; \
} \
#server-browser-list td .server-map {\
margin-top : 10px; \
display : block; \
color : #a8a8a8; \
} \
#server-browser-list td .server-expansions { \
position : absolute; \
top : 0px; \
right : 0px; \
bottom : 0px; \
margin : -5px; \
} \
#server-browser-list td .server-expansions img { \
margin : 5px; \
} \
#server-browser-list td.column-buttons { \
width : 170px; \
text-align : center; \
vertical-align : middle; \
} \
#server-browser-list td.column-icon { \
width : 62px; \
vertical-align : middle; \
} \
#server-browser-list td.column-favourite { \
width : 32px; \
font-size : 32px; \
color : #444; \
text-align : center; \
vertical-align : middle; \
} \
#server-browser-list td.column-favourite span { \
cursor : pointer; \
} \
#server-browser-list td.column-favourite span.server-favourited, \
#server-browser-list td.column-favourite span:hover { \
color : #fff; \
} \
#server-browser-list .column-rank { \
text-align : center; \
vertical-align : middle; \
width : 20px; \
} \
#server-browser-list .column-expansions, \
#server-browser-list .column-players, \
#server-browser-list .column-spectators, \
#server-browser-list .column-commanders, \
#server-browser-list .column-tickrate, \
#server-browser-list .column-ping, \
#server-browser-list .column-country { \
text-align : center; \
vertical-align : middle; \
width : 70px; \
} \
#server-browser-list thead th {\
font-weight : bold; \
cursor : pointer; \
} \
#server-browser-list tbody tr:nth-child(2n+1) {\
background-color : #111 \
} \
#server-browser-close { \
position : absolute; \
right : 0px; \
top : 0px; \
cursor: pointer; \
} \
#server-browser-list .server-button { \
height : 42px; \
width : 42px; \
line-height : 42px; \
text-align : center; \
font-size : 18px; \
border : 1px solid #d5dde5; \
color : #d5dde5; \
background-color : transparent; \
margin : 0; \
padding : 0; \
outline: 0 none; \
white-space: nowrap; \
cursor : pointer; \
} \
#server-browser-list .server-button:hover { \
background-color : #222; \
} \
#server-browser-list .server-button:disabled { \
background-color : transparent !important; \
border-color : #555 !important; \
color : #555 !important; \
cursor : default; \
} \
#server-browser-list .server-button-join { \
border-color : #ff9900; \
color : #ff9900; \
} \
#server-browser-list .server-button-commander { \
border-color : #FFD189; \
color : #FFD189; \
} \
#server-browser-list .server-button-favourite { \
border-color : #fff; \
color : #fff; \
} \
#server-browser-list .server-button:nth-child(2n+1) { \
margin-left : 10px; \
margin-right : 10px; \
} \
#server-browser-list .column-ping .ping-low { \
color : #2CD42C; \
} \
#server-browser-list .column-ping .ping-medium { \
color : #DADA00; \
} \
#server-browser-list .column-ping .ping-high { \
color : #CE2E2E; \
} \
#server-browser-list .server-slots-free { \
color : #2CD42C; \
} \
#server-browser-list .server-slots-busy { \
color : #DADA00; \
} \
#server-browser-list .server-slots-full { \
color : #CE2E2E; \
} \
#server-browser-list .server-details { \
position : relative; \
margin : 10px; \
background-repeat : no-repeat; \
background-size : auto 100%; \
background-position : center center; \
} \
#server-browser-list .server-details-inner { \
padding : 40px; \
background : rgba(0, 0, 0, .8); \
z-index : 20; \
border : 1px solid rgba(255, 255, 255, .5); \
} \
#server-browser-list .server-details-inner > h4, \
#server-browser-list .server-details-inner > h3, \
#server-browser-list .server-details-inner > h1 { \
text-align : center; \
} \
#server-browser-list .server-detail-progress { \
font-size : 0; \
margin-left : -40px; \
margin-right : -40px; \
} \
#server-browser-list .server-detail-progress > div { \
width : 50%; \
display : inline-block; \
font-size : 20px; \
padding : 40px; \
box-sizing : border-box; \
} \
#server-browser-list .server-detail-progress > div:nth-child(1) { \
text-align : right; \
} \
#server-browser-list .server-detail-progress > div > span { \
display : block; \
} \
#server-browser-list .server-detail-progress > div > div { \
margin-top : 10px; \
margin-bottom : 10px; \
} \
#server-browser-list .server-details-score-table { \
margin-top : 10px;\
background : #000; \
}\
#server-browser-list .server-details-score-table .details-kills, \
#server-browser-list .server-details-score-table .details-deaths, \
#server-browser-list .server-details-score-table .details-score, \
#server-browser-list .server-details-score-table .details-rank { \
width: 60px; \
}\
#server-browser-list .server-details-score-table .details-position { \
width: 30px; \
}\
#server-browser-list .server-details-inner .home { \
color: #94cdf3; \
}\
#server-browser-list .server-details-inner .away { \
color: #ff9f80; \
}\
#server-browser-search-message { \
color : #666; \
font-size : 24px; \
text-align : center; \
padding : 40px; \
}\
";
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// GLOBALS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
var $overlay = null;
var $list = null;
var $header = null;
var $details = null;
var $message = null;
var resourceURL = 'http://eaassets-a.akamaihd.net/bl-cdn/cdnprefix/3e6eb3a07b3e1e9faf139e2ab81a33e753a1a318/public/';
var serverURL = 'http://' + window.location.hostname + '/bf4/servers/getServers/pc/';
var snapshotURL = ' http://keeper.battlelog.com/snapshot/';
var favouritesURL = 'http://' + window.location.hostname + '/bf4/servers/favourites/pc/';
var factions = {
0: 'US',
1: 'RU',
2: 'CN',
};
var presets = {
1: 'Normal',
2: 'Hardcore',
4: 'Infantry Only',
8: 'Custom',
16: 'Immersive',
32: 'Classic',
64: 'New Players Only'
};
var columns = {
favourite: ' ',
rank: ' ',
icon: ' ',
name: '<span class="sort-by-name">Name</span> / <span class="sort-by-map">Map</span> / <span class="sort-by-mode">Mode</span>',
map: '&nbsp;',
mode: '&nbsp;',
country: 'Country',
players: 'Players',
spectators: 'Spectators',
commanders: 'Commanders',
tickrate: 'Tickrate',
ping: 'Ping',
buttons: '&nbsp;'
};
var extractors = {
'column-name': function(node, table, cellIndex){ return $(node).find('div').attr('data-name'); },
'column-map': function(node, table, cellIndex){ return $(node).text(); },
'column-mode': function(node, table, cellIndex){ return $(node).text(); },
'column-country': function(node, table, cellIndex){ return $(node).find('img').attr('title'); },
'column-players': function(node, table, cellIndex){ return $(node).find('div').attr('data-free'); },
'column-spectators': function(node, table, cellIndex){ return $(node).find('div').attr('data-free'); },
'column-commanders': function(node, table, cellIndex){ return $(node).find('div').attr('data-free'); },
'column-tickrate': function(node, table, cellIndex){ return $(node).text(); },
'column-ping': function(node, table, cellIndex){ return $(node).find('span').text(); }
};
var colrefs = [
function(iter, isFavourite) {
var span = $('<span>★</span>');
if ( isFavourite ) {
span.addClass('server-favourited');
}
span.on('click', function() {
toggleFavourite(iter, function(err, resposne) {
if ( resposne === 'removed' ) {
span.removeClass('server-favourited');
} else if ( resposne === 'added' ) {
span.addClass('server-favourited');
}
});
});
return span;
},
function(iter) {
var type = iter.serverType == 1 ? 'Official' : 'Unofficial';
var notes = [];
if ( iter.ranked ) {
notes.push('Ranked');
}
if ( iter.hasPassword ) {
notes.push('Private');
}
if ( notes.length ) {
type += ' (' + notes.join(', ') + ')';
}
var icon = $('<span class="icon-servertype icon-servertype-' + iter.serverType.toString() + '"></span>');
icon.attr('title', type);
return icon;
},
function(iter) {
var img = $('<img />');
img.attr({
src: resourceURL + 'base/bf4/map_images/62x42/' + iter.map.toLowerCase() + '.jpg',
title: iter.map,
alt: iter.map
});
return img;
},
function(iter) {
var detail = [
$S.callFunction("common.mapinfo", iter.game, iter.map, null , {}).name,
presets[iter.preset],
$S.callFunction("gamedata.localizedGameMode", iter.game, iter.mapMode, null , {}) //iter.mapMode
];
var outer = $('<div></div>').attr('data-name', iter.name);
var name = $('<p class="server-name">' + iter.name + '</p>');
if ( iter.punkbuster ) {
name.append($('<span class="icon icon-punkbuster"></span>'));
}
if ( iter.fairfight ) {
name.append($('<span class="icon icon-fairfight"></span>'));
}
var escapedName = iter.name.replace(/[^0-9A-z\s]/g, '').replace(/\[|\]/g, '').replace(/\s+/g, ' ').replace(/\s/g, '-');
var fullLink = $('<a class="icon icon-white icon-home" href="/bf4/servers/show/pc/' + iter.guid + '/' + escapedName + '"></a>');
fullLink.on('click', function(ev) {
ev.stopPropagation();
});
name.append(fullLink);
var map = $('<p class="server-map">' + detail.join(' &middot; ') + '</p>');
var expansions = $('<div class="server-expansions"></div>');
if ( parseInt(iter.gameExpansion, 10) ) {
iter.gameExpansions.forEach(function(e) {
e = parseInt(e, 10);
if ( ([268435456, 0]).indexOf(e) < 0 ) {
var src = resourceURL + 'common/tags/expansion/' + e + '.gif';
var img = $('<img src="' + src + '" />');
expansions.append(img);
}
});
}
outer.append(name);
outer.append(map);
name.append(expansions);
return outer;
},
function(iter) {
return iter.map;
},
function(iter) {
return iter.mapMode;
},
function(iter) {
var country = iter.country;
var img = $('<img />');
img.attr({
'src': resourceURL + 'common/flags/' + country + '.gif',
'title': country,
'alt': country
});
return img;
},
function(iter) {
return createPlayerSlots(iter.slots);
},
function(iter) {
return createSpectatorSlots(iter.slots);
},
function(iter) {
return createCommanderSlots(iter.slots);
},
function(iter) {
return iter.tickRate;
},
function(iter) {
var span = $('<span id="server-ping-' + iter.guid + '" class="">--</span>');
return span;
},
function(iter) {
var outer = $('<div></div>');
var joinButton = $('<button class="server-button server-button-join">►</button>').on('click', function(ev) {
ev.preventDefault();
joinServer(iter, 1); //gameServerRoleTypes.PLAYER);
});
var cmdButton = $('<button class="server-button server-button-commander">C</button>').on('click', function(ev) {
ev.preventDefault();
joinServer(iter, 2); //gameServerRoleTypes.COMMANDER);
});
var specButton = $('<button class="server-button server-button-spectate">S</button>').on('click', function(ev) {
ev.preventDefault();
joinServer(iter, 4); //gameServerRoleTypes.SPECTATOR);
});
if ( !iter.slots[8] || !iter.slots[8].max ) {
specButton.attr('disabled', 'disabled');
}
if ( !iter.slots[4] || !iter.slots[4].max ) {
cmdButton.attr('disabled', 'disabled');
}
outer.append(specButton);
outer.append(cmdButton);
outer.append(joinButton);
return outer;
}
];
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// INJECTIONS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function injectStylesheet() {
if ( document.getElementById('ServerBrowserCSS') ) {
return;
}
var el = document.createElement('style');
el.setAttribute('type', 'text/css');
el.setAttribute('id', 'ServerBrowserCSS');
el.innerHTML = stylesheet;
document.getElementsByTagName('head')[0].appendChild(el);
}
function createMenuEntry() {
if ( document.getElementById('open-fullscreen-server-browser') ) {
return;
}
var ul = $('.main-nav ul');
var li = $('<li id="open-fullscreen-server-browser"></li>');
var a = $('<a href="javascript:void(0);">☲</a>');
a.on('click', function() {
showBrowser();
});
li.append(a);
ul.append(li);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// API
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function getServers(filter, callback) {
filter = filter || {};
callback = callback || function() {};
var result = [];
var found = {};
var dupes = 0;
var page = 1;
function _done() {
console.warn('ServerBrowser', '=>', result);
getFavourites(function(favourites) {
callback(result, favourites);
});
}
function _next(offset) {
setHeaderText('(loading page ' + page.toString() + ')');
page++;
var url = serverURL + '?offset=' + offset + '&count=30';
$.getJSON(url, function(response) {
response.data.forEach(function(iter) {
// It seems battlelog is returning duplicates!
if ( found[iter.guid] ) {
dupes++;
return;
}
found[iter.guid] = true;
result.push(iter);
});
if ( response.data.length < 30 ) {
return _done();
}
_next(offset+30);
});
}
_next(0);
}
function getFavourites(callback) {
function _done(result) {
console.warn('getFavourites()', '=>', result);
callback(result);
}
setHeaderText('(loading favourites)');
$.ajax({
url: favouritesURL,
dataType: 'json',
cache: false,
beforeSend: function(xhr) {
xhr.setRequestHeader("X-AjaxNavigation", '1');
},
success: function(data, textStatus, XMLHttpRequest) {
if ( data && data.globalContext && data.globalContext.favGuids ) {
return _done(data.globalContext.favGuids);
}
_done(false);
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
_done(false);
}
});
}
var pingQueueIndex = 0;
function queuePing(list) {
launcher.cancelPingServers();
var pingThese = {};
list.forEach(function(iter) {
pingThese[iter.guid] = iter.ip;
});
var total = 0;
function myPingEvent(event, response) {
Object.keys(response).forEach(function(id) {
var grade = 'none';
if ( response[id] <= 45 ) {
grade = 'low';
} else if ( response[id] >= 46 && response[id] <= 100 ) {
grade = 'medium';
} else if ( response[id] >= 101 ) {
grade = 'high';
}
$('#server-ping-' + id).addClass('ping-' + grade).html(response[id]);
$('#server-browser-table').trigger('update');
total++;
});
if ( total >= list.length-1 ) {
$('#server-browser-table').trigger('update');
launcher.unregisterForEvent('pingResult', myPingEvent);
}
}
launcher.registerForEvent('pingResult', myPingEvent);
launcher.pingServers(pingThese);
}
function joinServer(iter, role) {
var friendId = null;
gamemanager.joinServerByGuid(iter.guid, iter.platform, iter.game, friendId, role);
hideBrowser();
}
function toggleFavourite(iter, cb) {
var url = serverbrowserwarsaw.function_addFavoriteServerUrl();
$.ajax({
url: url,
dataType: "json",
type: "POST",
data: {
guid: iter.guid,
platform: base.function_platform(iter.platform).toLowerCase(),
"post-check-sum": S.globalContext.session.postChecksum
}
}).complete(
base.onComplete(function (success, response) {
if (success) {
S.globalContext.favGuids.push(iter.guid);
if (response.message == "GAMESERVERBOOKMARK_REMOVED") {
cb(true, 'removed');
} else {
cb(true, 'added');
}
cb(true);
} else {
cb(false);
}
}, false)
);
}
function getDetails(iter, callback) {
var url = 'http://battlelog.battlefield.com/bf4/servers/getNumPlayersOnServer/pc/' + iter.guid + '/';
$.get(url, function(response) {
launcher.queryServer(iter, function(queryInfo) {
console.warn('getDetails()', '=>', queryInfo);
var update = false;
switch (queryInfo.status) {
case "OK":
// 205 is a bug that will make the scoreboard look bad
if ((queryInfo.result.teams.length > 0) && (queryInfo.result.teams[0].status.teamId == 205)) {
update = 'No data';
} else if (queryInfo.result.isEoR) {
update = 'No data';
}
break;
case "ROUND_NOT_STARTED":
update = 'Round not started';
break;
default:
update = 'Error: ' + queryInfo.status;
break;
}
callback(response, update, queryInfo.result);
});
});
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// UI
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function createPlayerSlots(slots) {
var current = slots[2] ? slots[2].current : 0;
var max = slots[2] ? slots[2].max : 0;
var players = current.toString() + '/' + max.toString();
var free = max - current;
var freeClass = 'full';
if ( free > 0 ) {
if ( free >= (max * 0.5) ) {
freeClass = 'free';
} else {
freeClass = 'busy';
}
}
var label1 = $('<span class="server-slots-' + freeClass + '">' + players + '</span>');
label1.attr('title', free.toString() + ' free slots');
current = slots[1] ? slots[1].current : 0;
max = slots[1] ? slots[1].max : 0;
var queue = current.toString() + '/' + max.toString();
var label2 = $('<span class="">(' + queue + ')</span>');
var outer = $('<div></div>');
outer.attr('data-free', free.toString());
outer.append(label1);
outer.append('<br />');
outer.append(label2);
return outer;
}
function createSpectatorSlots(slots) {
var outer = $('<div></div>');
var current = slots[8] ? slots[8].current : 0;
var max = slots[8] ? slots[8].max : 0;
outer.attr('data-free', (max - current).toString());
outer.html(current.toString() + '/' + max.toString());
return outer;
}
function createCommanderSlots(slots) {
var outer = $('<div></div>');
var current = slots[4] ? slots[4].current : 0;
var max = slots[4] ? slots[4].max : 0;
outer.attr('data-free', (max - current).toString());
outer.html(current.toString() + '/' + max.toString());
return outer;
}
function renderList(headerOnly) {
$list.empty().append($('<table class="tablesorter" id="server-browser-table"><thead></thead><tbody></tbody></table>'));
var thead = $list.find('thead');
var tbody = $list.find('tbody');
function createHeader() {
var row = $('<tr></tr>');
Object.keys(columns).forEach(function(c) {
var col = $('<th class="column-' + c + '">' + columns[c] + '</th>');
row.append(col);
});
thead.append(row);
row.find('span.sort-by-name').on('click', function(ev) {
ev.stopPropagation();
var dir = thead.find('.column-name').hasClass('headerSortDown') ? 1 : 0;
$('#server-browser-table').trigger('sorton', [[[3,dir]]]);
});
row.find('span.sort-by-map').on('click', function(ev) {
ev.stopPropagation();
var dir = thead.find('.column-map').hasClass('headerSortDown') ? 1 : 0;
$('#server-browser-table').trigger('sorton', [[[4,dir]]]);
});
row.find('span.sort-by-mode').on('click', function(ev) {
ev.stopPropagation();
var dir = thead.find('.column-mode').hasClass('headerSortDown') ? 1 : 0;
$('#server-browser-table').trigger('sorton', [[[5,dir]]]);
});
}
function createRow(iter, favourites) {
var row = $('<tr class="server-entry"></tr>');
var isFavourite = (favourites || []).indexOf(iter.guid) >= 0;
row.on('click', function(ev) {
ev.preventDefault();
ev.stopPropagation();
openDetails(iter, row);
});
colrefs.forEach(function(c, idx) {
var result = c(iter, isFavourite);
var col;
var colName = Object.keys(columns)[idx];
if ( typeof result === 'string' ) {
col = $('<td class="column-' + colName + '">' + result + '</td>');
} else {
col = $('<td class="column-' + colName + '"></td>').append(result);
}
row.append(col);
});
tbody.append(row);
}
if ( $details ) {
$details.remove();
$details = null;
}
$message.hide();
$list.hide();
createHeader();
if ( headerOnly ) {
return;
}
getServers(null, function(list, favourites) {
if ( list.length ) {
$list.show();
$message.hide();
} else {
$list.hide();
$message.show();
}
setHeaderText(list.length);
list.forEach(function(iter) {
createRow(iter, favourites);
});
initSort();
setTimeout(function() {
queuePing(list);
}, 10);
});
}
function hideDetails() {
if ( $details ) {
$details.remove();
$details = null;
}
$('#server-browser-detail-row').remove();
$('#server-browser-table').trigger('update');
}
function openDetails(iter, row) {
hideDetails();
$('#server-browser-list tbody tr').removeClass('active');
row.addClass('active');
var newRow = $('<tr id="server-browser-detail-row" class="active"></tr>');
var newCell = $('<td colspan="' + Object.keys(columns).length.toString() + '"></td>');
var container = $('<div class="server-details"></div>');
var inner = $('<div class="server-details-inner"></div>');
container.append(inner);
newCell.append(container);
newRow.append(newCell);
row.after(newRow);
function renderPlayerboard(players, table) {
players.forEach(function(p, i) {
var row = $('<tr></tr>');
row.append('<td class="details-position">' + (i+1).toString() + '</td>');
row.append('<td class="details-rank">' + p.rank + '</td>');
row.append('<td class="details-name">' + p.name + '</td>');
row.append('<td class="details-kills">' + p.kills + '</td>');
row.append('<td class="details-deaths">' + p.deaths + '</td>');
row.append('<td class="details-score">' + p.score + '</td>');
table.find('tbody').append(row);
});
}
function updateRow(slots) {
row.find('.column-players').empty().append(createPlayerSlots(slots));
row.find('.column-spectators').empty().append(createSpectatorSlots(slots));
row.find('.column-commanders').empty().append(createCommanderSlots(slots));
}
getDetails(iter, function(data, error, details) {
updateRow(data.slots);
queuePing([iter]);
var mapName = $S.callFunction("common.mapinfo", iter.game, iter.map, null , {}).name;
var gameMode = $S.callFunction("gamedata.localizedGameMode", iter.game, iter.mapMode, null , {});
var header = $('<h1>' + mapName + ' / ' + gameMode + ' / ' + presets[iter.preset] + '</h1>');
inner.append(header);
var imgSrc = resourceURL + 'base/bf4/map_images/992x345/' + iter.map.toLowerCase() + '.jpg';
container.css('backgroundImage', 'url(' + imgSrc + ')');
if ( error ) {
var message = $('<h3>' + error + '</h3>');
inner.append(message);
} else {
var homePercent = parseInt((details.teams[0].status.tickets / details.teams[0].status.ticketsMax) * 100, 10);
var homeTable = $('<table class="server-details-score-table"><thead><tr><th>#</th><th>Rank</th><th>Name</th><th>Kills</th><th>Deaths</th><th>Score</th></tr></thead><tbody></tbody></table>');
var homeProgressContainer = $('<div><span class="home">' + factions[details.teams[0].status.faction] + '</span></div>');
var homeProgress = $('<div class="progress-bar thicker home"><div class="progress-bar-inner home" style="width:' + homePercent + '%"></div></div>');
homeProgressContainer.append(homeProgress);
homeProgressContainer.append($('<span class="home">' + details.teams[0].status.tickets.toString() + '</span>'));
homeProgressContainer.append(homeTable);
renderPlayerboard(details.teams[0].players, homeTable);
var awayPercent = parseInt((details.teams[1].status.tickets / details.teams[1].status.ticketsMax) * 100, 10);
var awayTable = $('<table class="server-details-score-table"><thead><tr><th>#</th><th>Rank</th><th>Name</th><th>Kills</th><th>Deaths</th><th>Score</th></tr></thead><tbody></tbody></table>');
var awayProgressContainer = $('<div><span class="away">' + factions[details.teams[1].status.faction] + '</span></div>');
var awayProgress = $('<div class="progress-bar thicker away reversed"><div class="progress-bar-inner away" style="width:' + awayPercent + '%"></div></div>');
awayProgressContainer.append(awayProgress);
awayProgressContainer.append($('<span class="away">' + details.teams[1].status.tickets.toString() + '</span>'));
awayProgressContainer.append(awayTable);
renderPlayerboard(details.teams[1].players, awayTable);
var progress = $('<div class="server-detail-progress"></div>');
progress.append(homeProgressContainer);
progress.append(awayProgressContainer);
var hours = parseInt( details.roundTime / 3600 ) % 24;
var minutes = parseInt( details.roundTime / 60 ) % 60;
var seconds = details.roundTime % 60;
var timestamp = (hours < 10 ? "0" + hours : hours) + ":" + (minutes < 10 ? "0" + minutes : minutes) + ":" + (seconds < 10 ? "0" + seconds : seconds);
var time = $('<h4>' + timestamp + '</h4>');
inner.append(time);
inner.append(progress);
}
});
$('#server-browser-list').scrollTop(
row.offset().top - $('#server-browser-list').offset().top + $('#server-browser-list').scrollTop()
);
$details = newRow;
}
function initSort() {
$('#server-browser-table').tablesorter({
textExtraction: function(node) {
if ( extractors[$(node).attr('class')] ) {
return extractors[$(node).attr('class')](node);
}
return $(node).text();
}
}).on('sortEnd', function() {
hideDetails();
}).on('sortStart', function() {
hideDetails();
});
}
function createOverlay() {
if ( $overlay ) {
return;
}
$header = $('<h1><span class="title">Server Browser</span></h1>');
var refresh = $('<span class="refresh">↻</span>');
refresh.on('click', function() {
renderList();
});
$header.append(refresh);
var close = $('<span id="server-browser-close">x</span>');
close.on('click', function() {
hideBrowser();
});
$header.append(close);
$overlay = $('<div id="server-browser-overlay"></div>');
$list = $('<div id="server-browser-list"></div>');
var msg = 'No server(s) found. Press refresh, or re-apply/modify your <a href="http://battlelog.battlefield.com/bf4/servers/">filters</a>';
$message = $('<div id="server-browser-search-message">' + msg + '</div>');
setHeaderText(0);
renderList(true);
$overlay.append($header);
$overlay.append($message);
$overlay.append($list);
$('body').append($overlay);
}
function setHeaderText(found) {
$header.find('span.title').html('Server Browser - ' + found.toString() + ' found');
}
function showBrowser() {
if ( !$overlay ) {
createOverlay();
} else if ( $overlay && $overlay.is(':visible') ) {
return;
}
$overlay.show();
renderList();
$('html').css('overflow-y', 'hidden').scrollTop(0);
}
function hideBrowser() {
if ( $overlay ) {
$overlay.hide();
}
$('html').css('overflow-y', 'scroll');
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// MAIN
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function init() {
injectStylesheet();
createMenuEntry();
//showBrowser();
}
setTimeout(function() { // Sometimes injection is wonky
init();
}, 1000);
init();
@hello-smile6
Copy link

Cool!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment