wisnoskij / Desura Collection Key Scraper

// ==UserScript==
// @name			Desura Collection Key Scraper
// @author			wisnoskij
// @version			2.8.1
// @namespace		http://indie-elitist.blogspot.ca/
// @description		Gets, stores, and displays the keys of your games. With the appropriate notifications for changes.
// @match			http://www.desura.com/collection*
// @include			http://www.desura.com/collection*
// @match			http://www.desura.com/games/*
// @include			http://www.desura.com/games/*
// @match			https://www.desura.com/collection*
// @include			https://www.desura.com/collection*
// @match			https://www.desura.com/games/*
// @include			https://www.desura.com/games/*
// @grant			GM_getValue
// @grant			GM_setValue
// @grant			GM_deleteValue
// @grant			GM_listValues
// @grant			GM_addStyle
// @grant			GM_setClipboard
// @require			https://gist.github.com/raw/2625891/waitForKeyElements.js
// @require			http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js
// @require			http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.1/jquery-ui.min.js
// ==/UserScript==

/** Change Log **
**	Fixed download issue by using more robouse encoding scheme (encodeURIComponent()). 
**/

//Make the icon list that will go below every game box
var icons = $('<div name="dcks_icons"><a title="Steam Keys" class="dcks_cat dcks_keyslist dcks_steamicon dcks_hide"></a> <a title="Desura Keys" class="dcks_cat dcks_keyslist dcks_desuraicon dcks_hide"></a> <a title="GOG Keys" class="dcks_cat dcks_keyslist dcks_gogicon dcks_hide"></a> <a title="In-Game Keys" class="dcks_cat dcks_keyslist dcks_ingameicon dcks_hide"></a> <a title="Misc Keys" class="dcks_cat dcks_keyslist dcks_miscicon dcks_hide"></a> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <a title="New Keys" class="dcks_keyslist dcks_newicon dcks_remove"></a><a title="Not Scanned" class="dcks_keyslist dcks_notscanedicon"></a><a title="Scrape Keys" class="dcks_keysearch"></a></div>'); //Create icons list
icons.find('.dcks_keysearch').bind('click', function(ev){ ev.preventDefault(); getKeys($(this).closest('.box')); }); //Make it search that particular game's Keys page

var catLabels = ['_STEAM', '_DESURA', '_GOG', '_INGAME', '_MISC'];  //GM_Value category names
var catNames = ['Steam', 'Desura', 'GOG', 'In-Game', 'Misc'];  //Nice readable names

var stats = $('<span id="dcks_stats">(<span id="dcks_gamecount"></span><span id="dcks_newexist" class="dcks_remove">:<span id="dcks_newcount"></span></span>)<span title="Scrape Keys" class="dcks_keysearch"></span><span id="dcks_proc" class="dcks_remove">(<span id="dcks_procdata"></span>)</span><span id="dcks_collmenu" style="font-size: small;cursor: pointer;">▼</span></span>');
stats.find('.dcks_keysearch').bind('click', function(ev){ ev.preventDefault(); getKeys($('.box:not(.dcks_remove)')); }); //Make it search all games

var mainMenu = $('<ul id="dcksmenu" style="z-index: 55;position: relative;top: 32px;left: 9px;width: 133px;display: none;">\
<li id="dcksmenu_filter">Filter\
	<ul><li id="dcksmenuclearfilter">Clear</li>\
	<hr>\
	<li id="dcksmenuscanstar">Unscanned & Starred</li>\
	<li id="dcksmenuunclaim">Unclaimed Keys</li>\
	<li id="dcksmenusteam">Steam Keys</li>\
	<li id="dcksmenudesura">Desura Keys</li>\
	<li id="dcksmenugog">GOG Keys</li>\
	<li id="dcksmenuingame">In-Game Keys</li>\
	<li id="dcksmenumisc">Misc Keys</li>\
</ul></li>\
<li id="dcksmenusearch">Search\
	<ul><li id="dcksmenusearchall">All</li>\
	<li id="dcksmenusearchunscanned">Unscanned</li>\
</ul></li>\
<hr>\
<li id="dcksclearallstar" class="dcks_ar">Clear All<span class="dcks_newicon dcks_keyslist"></span></li>\
<li id="dcksclearalldata" class="dcks_ar">Clear Storage<span class="dcks_notscanedicon dcks_keyslist"></span></li>\
<li id="dcksex">Import/Export\
	<ul><li id="dcksexdelete">Delete All</li>\
	<li id="dcksexexport">Export</li>\
	<li id="dckseximport">Import</li>\
	<li id="dcksexexportdl">Export Download List</li>\
</ul></li>\
<hr>\
<li id="dcksinfomenu">Info\
	<ul><li><b><a target="_blank" href="https://openuserjs.org/scripts/wisnoskij/Desura_Collection_Key_Scraper">Desura Collection Key Scraper</a></b></li>\
	<li>Version: ' + GM_info.script.version + '</li>\
	<li><i>By: Wisnoskij</i></li>\
</ul></li>\
<li id="dcksinfomenu">Controls\
	<ul><li>Your Collection(#1[:#2])<span title="Scrape Keys" class="dcks_keysearch"></span><span style="font-size: small;cursor: pointer;">▼</span>\
		<ul><li><b>#1</b>: The number of games in your collection.</li>\
		<li><b>#2</b>: The number of new/unscanned games in your collection.</li>\
		<li><span title="Scrape Keys" class="dcks_keysearch"></span> Scan entire selection</li>\
		<li><span style="font-size: small;cursor: pointer;">▼</span>: The Main Menu for this script (The one you are in right now).</li></ul></li>\
	<li><div name="dcks_icons"><a title="Steam Keys" class="dcks_cat dcks_keyslist dcks_steamicon"></a>(Steam) <a title="Desura Keys" class="dcks_cat dcks_keyslist dcks_desuraicon"></a>(Desura) <a title="GOG Keys" class="dcks_cat dcks_keyslist dcks_gogicon"></a>(GOG) <a title="In-Game Keys" class="dcks_cat dcks_keyslist dcks_ingameicon"></a>(In-Game) <a title="Misc Keys" class="dcks_cat dcks_keyslist dcks_miscicon"></a>(Misc)<br><a title="New Keys" class="dcks_keyslist dcks_newicon"></a>(New) <a title="Not Scanned" class="dcks_keyslist dcks_notscanedicon"></a>(Unscanned) <span title="Scrape Keys" class="dcks_keysearch"></span>(Scan)</div>\
		<ul><li>Greyed-out game icons mean that the key has been claimed to this account.<br>Non greyed-out icons means the the key still needs to be requested.</li>\
		<li>A <b>Star</b> icon means that that game has new or changed keys.\
			<ul><li>The <b>Star</b> sticks around until you dismiss it enmass through the script menu,<br>or individually my mouseing over a game\'s icons.</li>\
			<li>A greyed-out <b>Star</b> means that all the keys for that game have been claimed.</li></ul></li>\
		<li>An <b>Unscanned</b> icon means that the script has no data for this game.</li>\
		<li>Click the <b>Scan</b> icon to scan just that single game.</li>\
		<li>Click any of the game icons to open a menu to:\
			<ul><li>Toggle the game\'s Star on/off.</li>\
			<li>Clear the game\'s saved key data.</li>\
			<li>Look at the game\'s saved keys.\
				<ul><li>Clicking on any of these keys will transfer them to your clipboard, if supported.<br>Else a prompt will open up with the key.</li></ul></li></ul></li></ul></li>\
</ul></li>\
</ul>');
mainMenu.find('#dcksmenuclearfilter').bind('click', function(ev){ ev.preventDefault();
			$('.box.dcks_remove').removeClass('dcks_remove');
			setStats();
		});
mainMenu.find('#dcksmenuscanstar').bind('click', function(ev){ ev.preventDefault();
			$('.box').addClass('dcks_remove');
			$('a.dcks_newicon:not(.dcks_remove),a.dcks_notscanedicon:not(.dcks_remove)').closest('.box').removeClass('dcks_remove');
			setStats();
		});
mainMenu.find('#dcksmenuunclaim').bind('click', function(ev){ ev.preventDefault();
			$('.box').addClass('dcks_remove');
			$('.dcks_cat:not(.dcks_hide,.dcks_greyout)').closest('.box').removeClass('dcks_remove');
			setStats();
		});
mainMenu.find('#dcksmenusteam').bind('click', function(ev){ ev.preventDefault();
			$('.box').addClass('dcks_remove');
			$('.dcks_steamicon:not(.dcks_hide)').closest('.box').removeClass('dcks_remove');
			setStats();
		});
mainMenu.find('#dcksmenudesura').bind('click', function(ev){ ev.preventDefault();
			$('.box').addClass('dcks_remove');
			$('.dcks_desuraicon:not(.dcks_hide)').closest('.box').removeClass('dcks_remove');
			setStats();
		});
mainMenu.find('#dcksmenugog').bind('click', function(ev){ ev.preventDefault();
			$('.box').addClass('dcks_remove');
			$('.dcks_gogicon:not(.dcks_hide)').closest('.box').removeClass('dcks_remove');
			setStats();
		});
mainMenu.find('#dcksmenuingame').bind('click', function(ev){ ev.preventDefault();
			$('.box').addClass('dcks_remove');
			$('.dcks_ingameicon:not(.dcks_hide)').closest('.box').removeClass('dcks_remove');
			setStats();
		});
mainMenu.find('#dcksmenumisc').bind('click', function(ev){ ev.preventDefault();
			$('.box').addClass('dcks_remove');
			$('.dcks_miscicon:not(.dcks_hide)').closest('.box').removeClass('dcks_remove');
			setStats();
		});
mainMenu.find('#dcksmenusearchunscanned').bind('click', function(ev){ ev.preventDefault();
			getKeys($('a.dcks_notscanedicon:not(.dcks_remove)').closest('.box:not(.dcks_remove)'));
		});
mainMenu.find('#dcksmenusearchall').bind('click', function(ev){ ev.preventDefault();
			getKeys($('.box:not(.dcks_remove)'));
		});
mainMenu.find('#dcksclearallstar').bind('click', function(ev){ ev.preventDefault();
			if(confirm('Are you sure you want to Clear All Stars from every game in your current selection?')){
				$('.box:not(.dcks_remove)').find('a.dcks_newicon:not(.dcks_remove)').closest('[name=dcks_icons]').trigger('mouseenter');
			}
		});
mainMenu.find('#dcksclearalldata').bind('click', function(ev){ ev.preventDefault();
			if(confirm('Are you sure you want to Clear All Key Data from every game in your current selection?')){
				clearStorage($('.box:not(.dcks_remove)'));
			}
		});
mainMenu.find('#dcksexdelete').bind('click', function(ev){ ev.preventDefault();
			$('.box').removeClass('dcks_remove');
			if(confirm('Are you sure you want to Delete everything?')){
				deleteStorage();
				location.reload();
			}
			setStats();
		});
mainMenu.find('#dcksexexport').bind('click', function(ev){ ev.preventDefault();
			$('.box').removeClass('dcks_remove');	setStats();
			var list = GM_listValues();
			var datastr = '';
			var sep = askSep();
			var tmpval;
			for(var i=0; i<list.length; i++){
				tmpval = GM_getValue(list[i],'');
				if(typeof tmpval === 'string'){	tmpval = tmpval.replace(new RegExp('":"', 'g'), sep[1]).replace(new RegExp('","', 'g'), sep[2]);	}
				datastr += list[i] + sep[0] + tmpval + sep[3];
			}
			download('DCKS_data.csv', datastr);
		});
mainMenu.find('#dckseximport').bind('click', function(ev){ ev.preventDefault();
			$('.box').removeClass('dcks_remove');
			if(confirm('Are you sure you want to Import some data? This will DELETE all of your current data.')){
				deleteStorage();
				var input = prompt('Enter Data');
				var sep = askSep();
				var nvPair = input.split(sep[3]);
				if(nvPair[nvPair.length-1] === ''){	nvPair.splice(nvPair.length-1, 1);	}
				var tmpval;
				for(var i=0; i<nvPair.length; i++){
					tmpval = nvPair[i].split(sep[0]);

					GM_setValue(tmpval[0], tmpval[1].replace(new RegExp(sep[1], 'g'), '":"').replace(new RegExp(sep[2], 'g'), '","'));
				}
			}
			location.reload();
		});
mainMenu.find('#dcksexexportdl').bind('click', function(ev){ ev.preventDefault();
			$('.box').removeClass('dcks_remove');	setStats();
			var list = $('a.more');
			var datastr = '';
			var tmpval;
			var requests = [];
			proc = 0;
			for(var i=0; i<list.length; i++){
				proc++;
				setProc(stats);
				$.get(list.eq(i).prop('href'), function(data){
					var title = $(data).find('.title>a').prop('href').split('/').pop();
					var downloads = $(data).find('a.clear[href*="gamedownloads"]');
					for(var i=0; i<downloads.length; i++){
						datastr += title + '\t'
									+ downloads.eq(i).find('.icons').justtext() + '\t'
									+ downloads.eq(i).find('.desc').justtext() + '\t'
									+ downloads.eq(i).find('.buy').justtext() + '\t'
									+ downloads.eq(i).prop('href') + '\r\n';
					}
				}, 'html')
				.always(function(){
					proc--;
					setProc(stats);
					if(proc === 0){
						download('DCKS_downlod_links.csv', datastr);
					}
				}).fail(function(data, textStatus, jqXHR){
					alert(textStatus);
				});
			}
		});
mainMenu.menu();

jQuery.fn.justtext = function(){
	return $(this).clone().children().remove().end().text();
};

//Run for every BOX element that gets loaded by ajax
waitForKeyElements(".box", main);
waitForKeyElements(".boxrowheading", collection);

function download(name, data){
	data = data.replace(/#/g, "");
	
	var a = document.createElement('a');
	a.setAttribute('download', name);
	a.setAttribute('title', name);
	a.setAttribute('name', name);
	a.href = 'data:application/csv;charset=utf-8;filename='+name+','+encodeURI(data);
	a.innerHTML = name;
	a.style.display = 'none';
	document.body.appendChild(a);
	a.click();
}
function deleteStorage(){
	var list = GM_listValues();
	for(var i=0; i<list.length; i++){
		GM_deleteValue(list[i]);
	}
}
function askSep(){
	var sep = ['"="', '":"', '","', '\\r\\n'];
	var ask = ['Enter Name/Value Pair Separator:', 'Enter Title:Key Separator:', 'Enter Keys Separtor:', 'Enter Name/Value Pair Ending:'];
	sep[0] = prompt(ask[0], sep[0]);
	sep[3] = prompt(ask[3], sep[3]).replace(new RegExp('\\\\n', 'g'), '\n').replace(new RegExp('\\\\r', 'g'), '\r')
	sep[1] = prompt(ask[1], sep[1]);
	sep[2] = prompt(ask[2], sep[2]);
	return(sep);
}

$("head").append('<link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.1/themes/ui-lightness/jquery-ui.min.css" rel="stylesheet" type="text/css">'); //JQuery UI Theme

$('html').bind('click',
function(ev){
	var clickNode = $(ev.target);
	if(clickNode.closest('.ui-menu').length === 0){  //Do not do anything, if you clicked a menu
		$('.ui-menu').hide();

		if(clickNode.closest('.dcks_keyslist').length){
			clickNode.closest('.box').find('>.ui-menu').show();  //Show the menu if you clicked its icon list
		}else if(clickNode.closest('#dcks_collmenu').length){
			$('#dcksmenu').show();  //Show the menu if you clicked its icon list
		}
	}
}); //Close all menus on click, open one if its icons were clicked. If a menu was clicked, do not close it.

window.onhashchange = function(){	setStats();	proc = 0; };

//The main function that gets called one for every loaded game box
function main(box){
	var gameId = getGameId(box);
	box.attr('id', gameId);
	box.append(setIcons(icons.clone(true), gameId));  //Clone generic icons, set them for this specific game, then append to game box
	box.append(setMenu(gameId));
	box.find('>#' + gameId + '_dcksmenu').menu(); //Make menu
	setStats();	//Redundant except for Alpha/favourites
}

function collection(collNode){
	setStats();
	collNode.append(stats);
	collNode.after(mainMenu);
}

function setStats(){
	stats.find('#dcks_gamecount').html($('.box:not(.dcks_remove)').length);
	setNew();
}

function setNew(){
	var count = $('.box:not(.dcks_remove)').find('a.dcks_newicon:not(.dcks_remove),a.dcks_notscanedicon:not(.dcks_remove)').length;
	if(count > 0){
		stats.find('#dcks_newexist').removeClass('dcks_remove');
	}else{
		stats.find('#dcks_newexist').addClass('dcks_remove');
	}
	stats.find('#dcks_newcount').html(count);
}

function setProc(statsNode){
	if(proc > 0){
		statsNode.find('#dcks_proc').removeClass('dcks_remove');
	}else{
		statsNode.find('#dcks_proc').addClass('dcks_remove');
	}
	statsNode.find('#dcks_procdata').html(proc);
	return(statsNode);
}

//Set the icons based on stored data
function setIcons(gameIcons, gameId){
	var status = GM_getValue(gameId, null);
	
	if(status !== null){ //Never scanned, don't need to change anything so skip
		gameIcons.find('.dcks_notscanedicon').addClass('dcks_remove'); //remove the Not Scanned icon
		
		switch(status){ //Set the new star
			case(0): //Nothing new this scan, or nothing found. Do nothing
			break;
			case(1): //Newly scanned content(greyed out star)
				gameIcons.find('.dcks_newicon').removeClass('dcks_remove').addClass('dcks_greyout');
				gameIcons.bind('mouseenter', function(){GM_setValue(gameId, 0); gameIcons.unbind('mouseenter').find('.dcks_newicon').addClass('dcks_remove'); setNew(); });
			break;
			case(2): //New content(never before retrieved keys)
				gameIcons.find('.dcks_newicon').removeClass('dcks_remove');
				gameIcons.bind('mouseenter', function(){GM_setValue(gameId, 0); gameIcons.unbind('mouseenter').find('.dcks_newicon').addClass('dcks_remove'); setNew(); });
			break;
		}
		
		var data, node;
		for(var index=0; index<catLabels.length; index++){ //Set the category icons
			data = GM_getValue(gameId + catLabels[index], null);
			
			if(data !== null){  //if no data skip
				node = gameIcons.find('a').eq(index);
				node.removeClass('dcks_hide');
				
				if(data.indexOf('":"Not Yet Reclaimed') === -1){  //If there exists non reclaimed keys.
					node.addClass('dcks_greyout');
				}
			}
		}
	}
	return(gameIcons);
}

var clearStorageNode = $('<li class="dcks_ar">Clear<span class="dcks_notscanedicon dcks_keyslist"></li>').bind('click', function(ev){	ev.preventDefault(); clearStorage($(this).closest('.box')); });
var toggleStarNode = $('<li class="dcks_ar">Toggle<span class="dcks_newicon dcks_keyslist"></span></li>').bind('click', function(ev){	ev.preventDefault(); toggleStar($(this).closest('.box')); });
var menuTempDL = $('<ul name="dcks_menu"><hr></ul>').prepend(toggleStarNode, clearStorageNode).hide();
var menuTempDT = $('<li><ul></ul></li>\n').bind('click',
		function(ev){	ev.preventDefault();
			var keyNode = $(this).find('li.ui-menu-item:eq(0)');
			if(typeof GM_setClipboard === "function"){
				GM_setClipboard(keyNode.find('.dcks_key').text(), 'text');
			}else{
				prompt(keyNode.find('.dcks_title').text(), keyNode.find('.dcks_key').text());
			}
		});
var menuTempDD = $('<li></li>\n');
//Set the menu based on the stored data
function setMenu(gameId){
	var menu = menuTempDL.clone(true).attr('id', gameId + '_dcksmenu');
	var tmpNode, data;
	
	for(var i=0; i<catNames.length; i++){ //It better be the same length
		data = GM_getValue(gameId + catLabels[i], null);

		if(data !== null){
			tmpNode = menuTempDT.clone(true).prepend(catNames[i]);
			data = data.split('","');
            
			for(var j=0; j<data.length; j++){
				data[j] = data[j].split('":"');
				tmpNode.find('ul').append(menuTempDD.clone().html('<span class="dcks_title">' + data[j][0] + '</span> - <span class="dcks_key">' + data[j][1] + '</span>'));
			}
			menu.append(tmpNode);
		}
	}
	return(menu);
}

function toggleStar(box){
	var gameId, curS;
	for(var i=0; i<box.length; i++){
		gameId = box.eq(i).attr('id');
		curS = GM_getValue(gameId, null);
		if(curS === 0){
			GM_setValue(gameId, 2);
		}else if(curS === null){
			return(false);
		}else{
			GM_setValue(gameId, 0);
		}
		propigate(gameId);
	}
}

function clearStorage(box){
	var gameId;
	for(var i=0; i<box.length; i++){
		gameId = box.eq(i).attr('id');
		GM_deleteValue(gameId);
		for(var j=0; j<catLabels.length; j++){
			GM_deleteValue(gameId + catLabels[j]);
		}
		propigate(gameId);
	}
}

//Get game id, what it is called in the website url. Example: "ice-cream-surfer"
function getGameId(gameNode){
	return(gameNode.find('.btnsfour>a:eq(1)').attr('href').split('/')[2]); //Select the keys anchor (Could use "nth-of-type" for multiple game selection). Extract the gameid.
}

var proc = 0;
//Send a get request to the game key URL
function getKeys(element){
	element.addClass('dcks_processing');
	var url;
	for(var i=0; i<element.length; i++){
		processing(element.eq(i));
		url = '/games/' + getGameId(element.eq(i)) + '/keys/';
		$.get(url, process_html, 'html')
			.fail(	function(){	proc--;	element.removeClass('dcks_processing');	alert("Getting (" + url + ") failed.");	setProc(stats);	});
	}
}

function processing(element){
	proc++;
	setProc(stats);
}

//Process the key page data
function process_html(data){
	var content = $(data).find('.content');
	var gameId = content.find('>p:first-of-type>a:first-of-type').attr('href').split('/')[2]; //Use the necessarily unique game URL as a unique identifier
	
	content = content.find('>span, >a');
	var categoriedata = [[], [], [], [], []];
	var newkey = false, changekey = false;
	content.each(function(index, element){ //List of already reclaimed keys, and the list of not yet reclaimed keys
		var keycontent = $(element);
		var type = keycontent.find('.action').text();
		var title = keycontent.find('.heading>span').text();
		var value = keycontent.find('input[name=key]').val();
		if($.trim(keycontent.find('.price').text().toUpperCase()) == 'GET'){ //If the key is not available
			value = 'Not Yet Reclaimed';
			newkey = true;
		}
		
		switch(type){ //Sort all the keys into the categories
			case('Steam Key'):
				categoriedata[0].push(title + '":"' + value);
			break;
			case('Desura Key'):
				categoriedata[1].push(title + '":"' + value);
			break;
			case('GOG Key'):
				categoriedata[2].push(title + '":"' + value);
			break;
			case('In-Game Key'):
				categoriedata[3].push(title + '":"' + value);
			break;
			default:
				categoriedata[4].push(type + ' - ' + title + '":"' + value);
			break;
		}
	});
	
	categoriedata[0] = categoriedata[0].join('","'); //Convert the arrays into strings
	categoriedata[1] = categoriedata[1].join('","');
	categoriedata[2] = categoriedata[2].join('","');
	categoriedata[3] = categoriedata[3].join('","');
	categoriedata[4] = categoriedata[4].join('","');
	
	for(var index=0;index<catLabels.length;index++){ //Check current data against old stored data, replace and note if different
		if(GM_getValue(gameId + catLabels[index], '') !== categoriedata[index]){
			GM_setValue(gameId + catLabels[index], categoriedata[index]);
			changekey = true;
		}
	}
	
	if(changekey){ //Calculate the new game id status
		changekey = 1;
		if(newkey){
			changekey = 2;
		}
	}else{	changekey = 0;	}
	
	var oldstatus = GM_getValue(gameId, null);
	if(oldstatus !== changekey){ //Set the game status (star)
		if((oldstatus === null) || (changekey > 0)){  //Don't remove the star just because scanned again. Wait for mouse-over.
			GM_setValue(gameId, changekey);
		}
	}
	
	propigate(gameId);
	proc--;
	setProc(stats);
}

function propigate(gameId){
	var propig = $('#' + gameId).removeClass('dcks_processing');
	propig.find('[name="dcks_icons"],[name="dcks_menu"]').remove();
	main(propig);
	setNew();
}

//Additional CSS, appended with acronym dcks if creating completely new class
GM_addStyle(
'#boxesholder{\
overflow: visible;\
top: -10px;\
}\
#footer{\
top: -10px;\
position: relative;\
}\
.box>ul{\
width: 76px;\
position: absolute;\
z-index: 9;\
}\
ul>li>ul.ui-menu{\
white-space: nowrap;\
z-index: 9;\
}\
.box{\
padding: 1px 10px !important;\
}\
.dcks_processing{\
opacity: 0.4;\
filter: alpha(opacity=40);\
}\
#dcks_proc{\
font-size: xx-small;\
vertical-align: super;\
}\
.ui-menu-item>.dcks_keyslist{\
float: left;\
margin-top: 2px;\
}\
.dcks_ar{\
text-align: right;\
}\
.dcks_keysearch{\
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAYAAABy6+R8AAAACXBIWXMAAAsSAAALEgHS3X78AAAAz0lEQVQokY3RsUpDMRQG4O9WKAiunZwKTsIFwQfwDQqCT+Lq6gP4AILgKhQEJ6e+gZs4OYjSohRcBeG4nAuhCdRACOTkC/9JRISIgBM84hMvuMZkqJdzAOcIvOfhOX7ygr5C6PGLe+wWhSN846mFLvPWKkqR4LDcH+EAq4j4Uo/nXKfl5ghv2O+6btJAx7l+bBb6jDDH3kZP62ZPeeAi4RI3eMjHiex3VqHinxb5Yq+4TTzA0wo1P5Fxxh7g2VZUwLsy6lbUgFf/Qgl3MMP4D7gTDyqkNgiQAAAAAElFTkSuQmCC);\
background-repeat: no-repeat;\
cursor: pointer;\
}\
a.dcks_keysearch{\
float: right;\
background-size: 12px Auto;\
width: 12px;\
height: 12px;\
}\
span.dcks_keysearch{\
width: 13px;\
height: 13px;\
background-size: 14px Auto;\
display: inline-block;\
}\
.dcks_greyout{\
opacity: 0.6;\
filter: alpha(opacity=60);\
}\
.dcks_hide{\
visibility:hidden;\
}\
.dcks_remove{\
display:none !important;\
}\
#dcks_searchall{\
width: 12px;\
height: 12px;\
background-size: 12px Auto;\
position: relative;\
top: 1px;\
}\
.dcks_keyslist{\
background-repeat: no-repeat;\
background-size: Auto 13px;\
display: inline-block;\
width: 13px;\
height: 13px;\
cursor: pointer;\
}\
.dcks_steamicon{\
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAARCQAAEQkBwOWiGAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAARoSURBVFiFxZdfiFRlGMZ/77czWlu7G5GhYhCVUnuRer5zZsW2cqMbTSQyilaKsLIs6A+E/QEtb4SiyLIywi4ityIkQTMo0LEs2W3OdwZruqu9CXYRNNxVYlznnLeLObOOtqP7D3tuBp7vfd/nmfOe837fJ6oKgIiI53mPi8gqwAJzmF4MAk5V90RRtENTYVFVPM+ba4zpAZZNs2gjHEySZE0URQNGROQSiwMsM8b0SPrYnxCRj6ex+CmgCAxxkVaq6rpM2vPpwHequiGKopKqJjUybe964GUgU58gIqvEWjtwIZfjwD/Ac2EY7qgRvu9nM5nM5b29vcM1zlprRWQncHNd7qBYa3UK4gBPh2G4HSAIguVJkmwWkVuBGcCfwGctLS1b8vl8xVp7k4gcAZpryWYqyiKy3zn3USr+uqp+KyKzReRTEXkLOA5sPnny5KGurq7LnHN/AK/U15iSgTiON6qq5nK5O1V1E7C7XC63FwqFJwuFwoYwDJeo6gvAkuHh4S0AzrltwNHpMFBpa2srpkZeAv6O43htqVQ6VR/knNsK7BaRZ33fb9YqoukwUMrn82UAEfFF5IdisXhirEAR2Q00GWMWpdTUDajq52lxAWao6ulGsUmSnE5/Z6Y5o7GTMiAi+6Moejs1olT/Uef8+fNnNoi/G6jFAXhTMXBMVR+uHzbpJL2ura3tjfSJjCIIghXAYyLyVRiGQym9eDR3EnNgZRiG+84nfd/vAbpV9bAxZhcwpKp3AI8AfzU1NXl9fX3HgyBYraq7JmMgBraGYfjiWIsiIr7vP6OqrwHXpPRp4Ms4jp8vFosnPM+bZYz5HZhVb+ADYAVw/Rh1K6q6Dfg+m80erh+tF4Lv+zeq6lUi8msYhmdSbg7QA3SdY7x2ILHW3iIiK4B7gE5gWFUfcM4dSAtky+WylEqlkfGYGBWo7rjdIvIecPV/1msG6tHZ2dlSqVSae3t7j3Z0dCyI43h7agogUtUNzrlDjURzudwNqvqUqvpUt+TWhgbHMjBGwUVJkiwHVgFLqH599zvnvj4/1vf9bmD7hUQnbKAeQRA8pKo7gePlcnlerSULFy68IpvNvg88OpF6JgiCtblcbvZ4EwqFwheqegCY1dzc3J6aWpzNZqOJigNkVPUTVVXf9yNgX5Ike6MoCi+SdwygUqm0Aqjqu8CCiYrD2UkoVF+WTcaYXzzPmwsQBMFtS5cuPefN9TxvrojcB8TGmCMiYqibbBNFZgxOjDH3Ah8CjIyMDFhrnYj8qKrXGmMepLr5vOmcG/I8zweunKyBRpOwP45jWywWT1hr14nIRmBeupYA75TL5Vfb29vj/v7+n6h+GZM2MOahVFX3tLa2rs7n85V0mHSIyGygLwzDQRERa+0WqqfdyWJQrLV7gZUNAn4TkfWFQuHnejIdTjuA26cgDvDNeC8mA1QvG2cAn7PtmBJUdZ1QbcMBLu3VDOCgc+4uo6qaJMka4OClFE+SZI2q6ugo/r+u5/8C4FUFsbigklwAAAAASUVORK5CYII=);\
width: 14px !important;\
}\
.dcks_desuraicon{\
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAAASCAYAAACw50UTAAAACXBIWXMAAAsSAAALEgHS3X78AAAEjUlEQVQ4jYVUS2hcZRQ+/+O+73+ncydkRqdhMqQ3N5kMI1HIpLmRuGhVEIqv+sAXLrrqouBSfBVx4UqotIKgIBVRjC4shApdFARFK92kaUPCNDakxpqbSZnJzNw/9/G76KSMpdUPDgcOh+8//3l8AP8DhBAFAFBVdch13W81TXN64/8FsssBABghhBFCpOsxQogIISLTNCcdx/nBsqwpy7IOtdvtXznnqwghqScXI4RQl+tf5AAAomtJr2UymWeHh4fPUEozm5ub3zHG9vf19b3COV9qt9vzd+TvctyuGGRZzhWLxS+iKLoZhuFfYRiucc5XVVUdGRgYeC9JkrhWq71Qr9dnbdt+emho6BuMMV1bWzseBMGiLMsDkiTlJUm6j1Jqr6ysvLazs/MnAgDIZDIvua775c7ODiCEgFIKt34I0Ol0NpaXl5/gnP/hOM5Xy8vLL8qyXBgeHp7Tdb1fCAFCCIjjGJIkAVmWYWlp6VXf909TAIBUKvW4ECJZXV096vv+Z5Ik9VNK7zcMY6zRaPwcx3GjVCr9pmnaYKlU+n1xcXHqypUr05Zlea1WayGKovUwDP/u6+t7fWho6JRlWY/5vn+aUkoZY+xgGIbJ1tbWbCaTeZlSanPOa5ubm9/HcdwYHR39RVGUQc55oChKYd++fWcWFhYeiqLItyxrxrKsmSiK6ltbW7NhGH7MGDtAKWXUNM0pTdOy9Xr9XBRF/sDAwClZllVCCFy9evXN9fX1DwkhthAiQQjRrmcAgPv7+48Wi8X3oyiCMAy57/ufNxqN87ZtHzRNcwqn0+nDCKF4Y2PjpGma05IkqUEQNMMwjAghDACSJEnaAIC7m4CTJGkBQIIxVsMwjDjnTVmWFdM0pzc2Nk4ihOI9e/Y8gw3DmAAAIoTgQRAsBUFQkySJCSGQqqolAEBxHPvdAQuEEMRxvAkASFXVMSEEkiSJtdvtpU6nczlJkgAAiGma+3GtVjvcbDYvjoyMzDHGZubn58vNZvM8IYTouj4OAKLdbs8jhES3JaLT6cwDgNB1/UFCCNne3j5/6dKlSiqVOjA6Onq20WhcrNVqzwEAAKU05brunOd5Ip/Pvw0AMDg4+InneULX9Qpj7OFqtSrGx8d5tVoVjLFpwzAe8DxPFIvFTwEA9u7d+47necJ13TlCSGpXO0jXQ6FQOOF5nnAcZxYhhPP5/Fuu654FAKhUKivValVUKpUVAADXdX/M5/PvIoSw4ziznueJQqFwokeTbl8/7hrkcrljk5OTolwuX6CU2rZtP6lpmsMYm5mYmLhpWdYjmqY5tm0/RSlNl8vlC5OTkyKXyx27k6sXaDeoKEohnU4fNAyjDAAgSZKFMca6ro9gjIkkSQwAwDCMcjqdflRRlEIP8W3h6pVN0d0Gyjm/RilN5/P544yxA3Ec8yRJAiFEgBBSMcYqIURpNpvnrl+//gHn/Fr3BqI7q70bMAAkhBDTtu3ns9nsG4ZhlIQQgBCCVqt1+caNGx/V6/Wv4zje3s2/B9c9H7hVAUIkm80eGRsb+ymbzR7pHRbcpb+7+AcRzOEBa7Dd+wAAAABJRU5ErkJggg==);\
width: 17px !important;\
}\
.dcks_gogicon{\
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsSAAALEgHS3X78AAACLElEQVQ4jZ2SO2hUURRF1znvzceMwiikcEQRVGwkOGCwEk1SCAFFG2MQVLCxESQghGAjNmI6BUGQBC3U3iqFdiK2gSGJweCH4HfGmXzI+95j8cwQB22yu3vZ+1zOXleq1epLVe1nE3LOvdLNhgFUtd83M+4+DdhzIA/Ap/mIG8MFxp+F7bufXxOunfa5PRGy/1ABgIWZiLGLRdQ5R7nicf5oyFBvSLnikaYp5YrHUG/IuSMh5MEvxGzrVi73BVw6sUZph2Bm+GZGvZkgIkSRUm8mOOdRb6aAIAKNZkKaCo2WYSbEkdBopTjnUIDadMD1RzFbthrffyWYGbXpgJHJmJHJmI+fI8I15d1cyJXxGBHlRysBwBcRJkZLAOSLRr2VoFpkYrSEmWFmqBZQVR7fzHyl7SmNjQOGxpfazX6YcagqItmOACICwJlbTXLFzLe44BARpFqtWpIkOOfa5lwuh6p2MudfPl9E8DyvHVh/vVP/8/kAzx9GHNyXMR+8EPPgjrF3dw6AufcRw1cLvHgSsWtnDsiGr6ymHD/ro2ZGWaGnL6KnL2Xxi9JdEKoDEdWBiEqXkKYplZJy7FTA6reEw/0htpRh9M0MXXUU8h5J6uGcw1tx5HOgarBsOCfQMqLISFspcSKw7LKPBDA7H/H2fhGIGByD2mzIm3sFQHhdC4AubM2ybgJD1cOCDONfFNaLcc79hVBV2+eNWFUVH5jyff+kc3+4dgQ6cXZo6jd1wxLPH4vlBAAAAABJRU5ErkJggg==);\
}\
.dcks_ingameicon{\
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAA5klEQVRIieXVQUoDQRCF4S/ZqGcQXIgGPIMuFARXuha8hgdwmVu58xLRIBLPoOJiXEw6RhmTeSMDgg8KuodH/dNVM1201wgTVC3jBeMgv+sgeYnXBDDCYwoZJIRAVVkMewIs9KcAV9juE7CPO+z2BYAd3CaQLj2IIAMc4BIba7xHOFza3+MYzw3eankzlf+hJSaaG7/wDOen6KpNbK0ylBKdtwCdzqNohhN1qb6rani2Vjc+j/6gbvZP+lKiVFP1mz+1MaeAKHkKmOEsSc7vvqBV+qfXdTL0OwEusBf44S0xp0P/HeMPWXZrqsn3BSEAAAAASUVORK5CYII=);\
}\
.dcks_miscicon{\
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABuUlEQVRIibXVMWtUQRQF4G/jQgptlAgGVBQjoo2VthY2msaYtCIWWigBxcZ/IIK1FqawEBtBQrDRVixTipFgQgyCSrBIFJLC3VjM2zC8zOwbgx64MMw958ybeXfutPTHMC7jEg5hfzW/gmXMYBrfGny2YQQv0MFmQ3Qq7kip+QR+FRjX4yfGmszvorsD83g3d2LDVjQew0sMJBaewyy+YxCHcQrHE9xu5fUqnjyItcQXvceZPjs+i3cJ3apQIFt4miAtY6iPeQ9tvE3op3qEI9LVcjMyGcQz4Ueu4zl2RflzCf1vobTdTiQ3cTQyuJ7Ij0b53RmPyTYuZrb+UPhhcDqR34jGezMeozCfWT0XXdyvGd3LcOdIV08uVnChZn6t2k2Kv0r5rf2BY5HxHjxq0Kz9zRFN1cw/FGg+DmBRGT5F4/M4WaBZJF+m9fgstItZLBVqJlvCRVuQ7kExpoXjhH240cDviO7Sk4KvGY/EJwr4j+PVcs1upwtsNbt2JfiCq/LtGq4I3ZNwRDl0K+7XVPKfPzgp/Ncns4chPBBac5PxesUteTu2YRi38EYo5Y0qFvBaeDMO9DP4A+C1MWXpJ5scAAAAAElFTkSuQmCC);\
}\
.dcks_newicon{\
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsSAAALEgHS3X78AAACS0lEQVQ4jZWSTUhUURzFf+/D0ZkxrcbxTSM5DKGEKSImpYsQaqMN4aKVkIsisGiTG0HQnUtxkyAUFX1sWuVOMlAwamELP5Ig08n8wiZnZBgdZ95797bwFRZj1B/O5tx7zj3c84fDR40On7uz+uD8EKD95d6h49l5d8tOvbkpgcL/FWvzg7UD5sqINFdG5Pxg7cBhKdRc5O2WE75Sw+jSjSC6EaTUMLqAgn810O9eDgx5qtoQ8TFEfAxPVRsL9+oe50qhA9raw6anirRqNOxqhA15XlyBAGJ3DgBX4DRFhd6rm/frLFQNG+2DVLS5sutvr+mAW1flleKmG978cD3StkD1ITaeISyxHzMzg7/9EYgtFDLsLY5XJ6dehQG3AhQBp5aHz7721YSOu0qKkUKAVEFxEksbFIGiqWRjSeJzy/HyzqlLwKIKpIHNUOf7yMzE5/FsLInqrkRKGymsfUgb1VNJZiPBzMTCeHnnVATYBNIqYAIxYKm5b7ZbmDpoRUhb/A7Fi0hrNPfNdgNLjsb82YIJJEZ7zzTmlVZgJT8ihQ0IQCCFQKQ+ofsrGO2tbgQSjgb9QCNKwOeuVz0F2NsxxE6Wva8JAArKj4HXRDtSglGcVw8oufbA5Q+dbBGpL2RWE6xNr0eDHZOtwY7J1rXp9WhmNYG0tvGXBVoAV66l8sVfRuS3J02ivz3cAzQAIQcN/e3hntjzC2LrxUUJ+H7FPmBwFAgDbuC780kp56wQ8AMlTmtRYPtPAxfgdbhdIAPIAw/lAx6H2wGyAD8AKT3q7OJ/X3kAAAAASUVORK5CYII=);\
width: 14px !important;\
}\
.dcks_notscanedicon{\
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABYklEQVR4Xn3TP0vDQBgG8PckqCj+qYIKKhQLgouDi0OXfpP2lvYztUvTzW/gJN3SqYtLQVSColCRSBUx2mh8rn0IR0nvgZcbkueX8MIpsZKK1HC0xR0fJS1MBsQse82ms500GhNkKU21UmoKjFhe6XYlDUP50nousIwPxEQ2RLQasryJ8mKlIiax78u7A1kD8kFEPYikBag/QSBbKDLyCSRyIKbzBsT7BvOHiTodGePcJbJaq8kvzpc8hB3T9WIACSY2Q+SAyDqRpxyEHf4BNZMhEAMWiRSAmOehjVidBQOM+n0DZPMM5BZFRraBHLbb9jvsEEio2fMIZGAhO0COLCSxdzDmDmYTcien3Mked3KtddbxLC0399zJGZF9IlGvN+moC5EqTr9Ur8tdqyXzUqxW5ZzIaxDIVbnMe4F0iBwDuXEgJSDmnUuWUdJKmCaREyCuDKYf8Ov2ZZpFxB2/YV3nf9DqsZVlYlPZAAAAAElFTkSuQmCC);\
}\
.dcks_keydetails table{\
width: 898px;\
border: 1px dotted #ccc;\
}\
.dcks_keydetails th{\
border-left: 1px dotted #ccc;\
border-right: 1px dotted #ccc;\
border-bottom: 1px dotted #000;\
font-size: 15px;\
line-height: 20px;\
font-weight: bold;\
color: #1480cd;\
padding-left: 6px;\
width: 179.6px;\
}\
.dcks_keydetails td{\
border-left: 1px dotted #ccc;\
border-right: 1px dotted #ccc;\
text-align: center;\
width: 179.6px;\
}\
.dcks_closekeydetail{\
float: right;\
padding-right: 2px;\
font-weight: bolder;\
font-size: large;\
cursor: pointer;\
}');