SG Game Tags

some tags of the game in Steamgifts.

As of 2016-07-28. See the latest version.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         SG Game Tags
// @namespace    https://steamcommunity.com/id/Ruphine/
// @version      3.2.2
// @description  some tags of the game in Steamgifts.
// @author       Ruphine
// @match        *://www.steamgifts.com/*
// @icon         https://cdn.steamgifts.com/img/favicon.ico
// @connect      steampowered.com
// @connect      ruphine.esy.es
// @require      https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/spectrum/1.8.0/spectrum.min.js
// @grant        GM_deleteValue
// @grant        GM_getValue
// @grant        GM_listValues
// @grant        GM_setValue
// @grant        GM_xmlhttpRequest
// ==/UserScript==

/* CSS */
const default_bundle = "#E9202A";
const default_card = "#3AA435";
const default_achievement = "#305AC9";
const default_wishlist = "#9335F1";
const default_linux = "#E67300";
const default_mac = "#777777";
const default_early = "#9FA027";
const default_hidden = "#A0522D";

var myCSS = '\
	.tags { \
		text-decoration: none; \
		border-radius: 4px; \
		padding: 2px 5px; \
		font-size: 8pt; \
		margin: 3px 3px 3px 0px; \
		text-shadow: none; \
		display: none; \
		white-space: nowrap; \
	} \
	.tags.tags-minimalist { \
		margin-right: 0; \
		margin-left: 5px; \
	}\
	.my__checkbox { cursor:pointer; padding:7px 0 } \
	.my__checkbox i { margin-right:7px } \
	.my__checkbox:not(:last-of-type) { border-bottom:1px dotted #d2d6e0 } \
	.my__checkbox:not(:hover) .form__checkbox__hover,.my__checkbox.is-selected .form__checkbox__hover,.my__checkbox:not(.is-selected) .form__checkbox__selected,.my__checkbox:hover .form__checkbox__default,.my__checkbox.is-selected .form__checkbox__default { \
		display:none \
	} \
';
myCSS += '.tags-bundle { background-color: ' + GM_getValue("bundle-1", default_bundle) + '; color: ' + GM_getValue("bundle-2", "#FFFFFF") + '}';
myCSS += '.tags-card { background-color: ' + GM_getValue("card-1", default_card) + '; color: ' + GM_getValue("card-2", "#FFFFFF") + '}';
myCSS += '.tags-achievement { background-color: ' + GM_getValue("achievement-1", default_achievement) + '; color: ' + GM_getValue("achievement-2", "#FFFFFF") + '}';
myCSS += '.tags-wishlist { background-color: ' + GM_getValue("wishlist-1", default_wishlist) + '; color: ' + GM_getValue("wishlist-2", "#FFFFFF") + '}';
myCSS += '.tags-linux { background-color: ' + GM_getValue("linux-1", default_linux) + '; color: ' + GM_getValue("linux-2", "#FFFFFF") + '}';
myCSS += '.tags-mac { background-color: ' + GM_getValue("mac-1", default_mac) + '; color: ' + GM_getValue("mac-2", "#FFFFFF") + '}';
myCSS += '.tags-early { background-color: ' + GM_getValue("early-1", default_early) + '; color: ' + GM_getValue("early-2", "#FFFFFF") + '}';
myCSS += '.tags-hidden { background-color: ' + GM_getValue("hidden-1", default_hidden) + '; color: ' + GM_getValue("hidden-2", "#FFFFFF") + '}';

$("head").append('<style type="text/css">' + myCSS + '</style>');

/* Constant Variables */
const linkBundle = "https://www.steamgifts.com/bundle-games/search?q=";
const linkCard = "http://www.steamcardexchange.net/index.php?inventorygame-appid-";
const linkAchievement = "http://steamcommunity.com/stats/"; // 424280/achievements/";
const linkHidden = "https://www.steamgifts.com/account/settings/giveaways/filters/search?q=";
const linkWishlist = "https://www.steamgifts.com/account/steam/wishlist/search?q=";

const linkGameAPI = "http://store.steampowered.com/api/appdetails?appids=";//filters=categories,platforms,genres&
const linkPackAPI = "http://store.steampowered.com/api/packagedetails?packageids=";
const linkBundleAPI = "http://ruphine.esy.es/steamgifts/GetBundleStatus.php"; //?AppID=325470
const linkUserAPI = "http://store.steampowered.com/dynamicstore/userdata/";

const ClassCard = "tags tags-card";
const TitleCard = "This game has trading cards";
const TextCard = "Trading Cards";

const ClassBundle = "tags tags-bundle";
const TitleBundle = "Bundled since ";
const TextBundle = "Bundled";

const ClassAchievement = "tags tags-achievement";
const TitleAchievement = "This game has steam achievements";
const TextAchievement = "Achievements";

const ClassHidden = "tags tags-hidden";
const TitleHidden = "This game is in your filter list";
const TextHidden = "Hidden";

const ClassWishlist = "tags tags-wishlist";
const TitleWishlist = "This game is in your Steam wishlist";
const TextWishlist = "Wishlist";

const ClassLinux = "tags tags-linux";
const TitleLinux = "Linux supported";
const TextLinux = "Linux";

const ClassMac = "tags tags-mac";
const TitleMac = "Mac supported";
const TextMac = "Mac";

const ClassEarly = "tags tags-early";
const TitleEarly = "This game is in early access state";
const TextEarly = "Early Access";

const THIS_URL = window.location.href;
const TIMEOUT = 0;
const CACHE_TIME = 6*60*60*1000; //6 hours

var cbCards = GM_getValue("cbCards", true);
var cbAchievement = GM_getValue("cbAchievement", true);
var cbBundled = GM_getValue("cbBundled", true);
var cbHidden = GM_getValue("cbHidden", true);
var cbWishlist = GM_getValue("cbWishlist", true);
var cbLinux = GM_getValue("cbLinux", false);
var cbMac = GM_getValue("cbMac", false);
var cbEarly = GM_getValue("cbEarly", false);

var cbTagStyle = GM_getValue("cbTagStyle", 1); //1 = full, 2 = minimalist

var BundledGames = GM_getValue("BundledGames", "");
var BundledCache = GM_getValue("BundledCache", 0);
var UserdataAPI = GM_getValue("UserdataAPI", "");
var UserdataCache = GM_getValue("UserdataCache", 0);
var GameData = GM_getValue("GameData", "");
var PackageData = GM_getValue("PackageData", "");

var rgWishlist;
var arrBundled;

if(cbBundled && BundledCache < Date.now() - CACHE_TIME) // Check if need to request bundle list from ruphine API
	getBundleList();
else if(cbWishlist && UserdataCache < Date.now() - CACHE_TIME) //6 hours. Check if need to request steam user api
	getUserdata();
else
	main();

function main()
{
	try {
		arrBundled = JSON.parse(BundledGames);
	}
	catch (e) {
		console.log("[SG Game Tags] Invalid json format for Bundle List");
		BundledGames = ""; GM_setValue("BundledGames", "");
		BundledCache = 0;  GM_setValue("BundledCache", 0);
	}

	try {
		rgWishlist = JSON.parse(UserdataAPI).rgWishlist;
	}
	catch (e) {
		console.log("[SG Game Tags] Invalid json format for Wishlist");
		UserdataAPI = ""; GM_setValue("UserdataAPI", "");
		UserdataCache = 0; GM_setValue("UserdataCache", 0);
	}

	if(GameData == "") PrepareJSON();
	else
	{
		GameData = JSON.parse(GameData);
		PackageData = JSON.parse(PackageData);
	}

	if(/www.steamgifts.com\/giveaways\/new/.test(THIS_URL)) // process giveaway creation page
		$(".js__autocomplete-data").on("DOMNodeInserted", NewGiveawayDivUpdated);
	else if(/www.steamgifts.com\/account\/settings\/giveaways$/.test(THIS_URL)) // process giveaway setting page
		initSetting();
	else if(/www.steamgifts.com\/($|giveaways$|giveaways\/search)/.test(THIS_URL)) // homepage and all search active giveaway
	{
		ProcessGiveawayListPage($(".giveaway__row-inner-wrap"));
		// handles element added later by endless scroll, add timeout to delay this function because it is triggered when ext SG runs
		setTimeout(function()
		{
			$(document).on("DOMNodeInserted", ".widget-container", function(e)
			{
				ProcessGiveawayListPage($(e.target).find(".giveaway__row-inner-wrap"));
			});
		}, TIMEOUT);

		if($(".featured__inner-wrap .global__image-outer-wrap--missing-image").length == 0 && $(".featured__inner-wrap a img").length > 0)
		{
			var URL = $(".featured__inner-wrap a img")[0].src;
			ProcessFeaturedGiveaway(URL);
		}
	}
	// user profile & group page excluding user trade and feedback and excluding group users, stats, and wishlist
	else if(/www.steamgifts.com\/(user|group)\//.test(THIS_URL) && !/user\/\w+\/(feedback|trade)/.test(THIS_URL) && !/group\/\w+\/\w+\/(users|stats|wishlist)/.test(THIS_URL)) // exclude 
	{
		ProcessGiveawayListPage($(".giveaway__row-inner-wrap"));
		// handles element added later by endless scroll
		setTimeout(function()
		{
			$(document).on("DOMNodeInserted", ".widget-container", function(e)
			{
				ProcessGiveawayListPage($(e.target).find(".giveaway__row-inner-wrap"));
			});
		}, TIMEOUT);
	}
	else if(/www.steamgifts.com\/giveaway\//.test(THIS_URL)) // giveaway page https://www.steamgifts.com/giveaway/FGbTw/left-4-dead-2
	{
		var URL = $(".featured__inner-wrap a")[0].href;
		ProcessFeaturedGiveaway(URL);
	}
	// https://www.steamgifts.com/sales*
	// https://www.steamgifts.com/sales/account/steam/games
	// https://www.steamgifts.com/sales/account/steam/wishlist
	// https://www.steamgifts.com/giveaways/created
	// https://www.steamgifts.com/giveaways/entered
	// https://www.steamgifts.com/giveaways/won
	// https://www.steamgifts.com/giveaways/wishlist
	// https://www.steamgifts.com/account/settings/giveaways/filters
	else if(/www.steamgifts.com\/(sales|account\/steam\/(games|wishlist)|giveaways\/(created|entered|won|wishlist)|account\/settings\/giveaways\/filters|group\/\w+\/\w+\/wishlist)/.test(THIS_URL))
		ProcessGameListPage();

	AddShortcutToSettingPage();
}

function ProcessFeaturedGiveaway(URL)
{
	if(cbBundled || cbCards || cbAchievement || cbHidden || cbWishlist || cbLinux || cbMac || cbEarly) // check if at least one tag enabled
	{
		var Name = $(".featured__heading__medium").text().substring(0,45); //letter after 45th converted to ...
		var Target = $(".featured__heading");

		ProcessTags(Target, URL, Name);
	}
}

function ProcessGiveawayListPage(scope) // giveaways list with creator name
{
	if(cbBundled || cbCards || cbAchievement || cbHidden || cbWishlist || cbLinux || cbMac || cbEarly) // check if at least one tag enabled
	{
		$(scope).each(function(index, element)
		{
			var URL = $(element).find("a.giveaway__icon").attr("href");
			if(URL != undefined)
			{
				var Name = $(element).find(".giveaway__heading__name").contents().filter(
					function() //return text without [NEW] and [FREE]
					{
						return this.nodeType === 3; //Node.TEXT_NODE
					}
				).slice(-1)[0].textContent.substring(0,40); //letter after 40th converted to ...
				var Target = $(element).find(".giveaway__heading");
				ProcessTags(Target, URL, Name);
			}
		});
	}
}

function ProcessGameListPage() // giveaways / games list
{
	if(cbBundled || cbCards || cbAchievement || cbHidden || cbWishlist || cbLinux || cbMac || cbEarly) // check if at least one tag enabled
	{
		$(".table__row-inner-wrap").each(function(index, element)
		{
			var URL;
			if(/www.steamgifts.com\/account\/settings\/giveaways\/filters/.test(THIS_URL))
				URL = $(element).find("a.table__column__secondary-link").text();
			else
				URL = $($(element).find(".global__image-inner-wrap")[0]).css('background-image');

			if(URL != undefined)
			{
				URL = URL.replace('url(', '').replace(')', '');
				var Name = $(element).find(".table__column__heading").text().substring(0,30);
				var Target = $(element).find(".table__column--width-fill > :first-child");

				if(/www.steamgifts.com\/sales/.test(THIS_URL)) Target.css("display", "block"); //because sales pages don't use <p> thus tags will appears in line with title

				ProcessTags(Target, URL, Name);
			}
		});
	}
}

function ProcessTags(Target, URL, Name)
{
	var ID = getAppIDfromLink(URL);
	Name = encodeURIComponent(Name); //encode special characters that may break search params
	var linkStore = "";
	if(isApp(URL))
		linkStore = "http://store.steampowered.com/app/" + ID;
	else if(isPackage(URL))
		linkStore = "http://store.steampowered.com/sub/" + ID;

	if(cbTagStyle == 1)
	{
		var tagBundle      = createTag(ClassBundle, TitleBundle, TextBundle, linkBundle+Name, Target);
		var tagCard        = createTag(ClassCard, TitleCard, TextCard, linkCard+ID, tagBundle);
		var tagAchievement = createTag(ClassAchievement, TitleAchievement, TextAchievement, linkAchievement+ID+"/achievements/", tagCard);
		var tagWishlist    = createTag(ClassWishlist, TitleWishlist, TextWishlist, linkWishlist+Name, tagAchievement);
		var tagLinux       = createTag(ClassLinux, TitleLinux, TextLinux, linkStore, tagWishlist);
		var tagMac         = createTag(ClassMac, TitleMac, TextMac, linkStore, tagLinux);
		var tagEarly       = createTag(ClassEarly, TitleEarly, TextEarly, linkStore, tagMac);
	}
	else
	{
		var tagBundle      = createTag(ClassBundle + " tags-minimalist", TitleBundle, "B", linkBundle+Name, Target);
		var tagCard        = createTag(ClassCard + " tags-minimalist", TitleCard, "T", linkCard+ID, Target);
		var tagAchievement = createTag(ClassAchievement + " tags-minimalist", TitleAchievement, "A", linkAchievement+ID+"/achievements/", Target);
		var tagWishlist    = createTag(ClassWishlist + " tags-minimalist", TitleWishlist, "W", linkWishlist+Name, Target);
		var tagLinux       = createTag(ClassLinux + " tags-minimalist", TitleLinux, "L", linkStore, Target);
		var tagMac         = createTag(ClassMac + " tags-minimalist", TitleMac, "M", linkStore, Target);
		var tagEarly       = createTag(ClassEarly + " tags-minimalist", TitleEarly, "E", linkStore, Target);
	}

	if(/www.steamgifts.com\/giveaway\//.test(THIS_URL)) //only trigger inside giveaway page, no need for homepage
	{
		if(cbTagStyle == 1)
			var tagHidden = createTag(ClassHidden, TitleHidden, TextHidden, linkHidden+Name, tagEarly);
		else if(cbTagStyle == 2)
			var tagHidden = createTag(ClassHidden + " tags-minimalist", TitleHidden, "H", linkHidden+Name, Target);

		getHiddenStatus(ID, Name, tagHidden);
	}

	if(isApp(URL))
		getSteamCategories(ID, tagCard, tagAchievement, tagLinux, tagMac, tagEarly);
	else if(isPackage(URL))
	{
		tagCard.setAttribute("href", "");
		tagAchievement.setAttribute("href", "");
		getSteamCategoriesFromPackage(ID, tagCard, tagAchievement, tagLinux, tagMac, tagEarly);
	}

	var type = isApp(URL) ? 'app' : 'sub';
	if(cbBundled && BundledGames != "")
		getBundleStatus(ID, type, tagBundle);
	if(cbWishlist && UserdataAPI != "" && rgWishlist.length > 0)
		getWishlistStatus(ID, tagWishlist);
}

function createTag(_class, title, text, href, divTarget)
{
	var tag = document.createElement("a");
	tag.setAttribute("target", "_blank");
	tag.setAttribute("class", _class);
	tag.setAttribute("title", title);
	tag.setAttribute("href", href);
	tag.innerHTML = text;

	if(cbTagStyle == 1 || /www.steamgifts.com\/giveaways\/new/.test(THIS_URL)) // full text below game title, use after, or bundle tag in giveaway creation page
		$(divTarget).after(tag);
	else if(cbTagStyle == 2) // minimalist beside game title use append
		$(divTarget).append(tag);
	return tag;
}

function displayElems(elems)
{
	$(elems).css("display", "inline-block");
}

function getSteamCategories(appID, tagCard, tagAchievement, tagLinux, tagMac, tagEarly, packID = "0")
{
	var needRequest = false;
	if(GameData[appID] == undefined || GameData[appID].game == undefined)
	{
		var template = {"cards": false, "achievement": false, "mac": false, "linux": false, "early_access": false, "last_checked": 0, "game": appID+""};
		GameData[appID] = template;
		needRequest = true;
	}
	else
	{
		var data = GameData[appID];
		if(cbCards && data.cards)
		{
			displayElems(tagCard);
			if(GameData[appID].game != "0")
				tagCard.setAttribute("href", linkCard+GameData[appID].game);
		}
		if(cbAchievement && data.achievement)
		{
			displayElems(tagAchievement);
			if(GameData[appID].game != "0")
				tagAchievement.setAttribute("href", linkAchievement+GameData[appID].game+"/achievements/");
		}
		if(cbMac && data.mac) displayElems(tagMac);
		if(cbLinux && data.linux) displayElems(tagLinux);

		if(data.last_checked < (Date.now() - (24 * 60 * 60 * 1000))) // 24 hours have passed since last checked
		{
			if((!data.cards && cbCards) || (!data.achievement && cbAchievement) || (!data.mac && cbMac ) || (!data.linux && cbLinux) || (data.early_access && cbEarly))
				needRequest = true;
		}
		else if(data.early_access && cbEarly) displayElems(tagEarly);
	}
	if(needRequest)
	{
		var link = linkGameAPI+appID;
		if(GameData[appID].last_checked !== 0) link += "&filters=categories,platforms,genres";
		// console.log("[SG Game Tags] requesting " + linkGameAPI+appID);

		GM_xmlhttpRequest({
			method: "GET",
			timeout: 10000,
			url: link,
			onload: function(data)
			{
				var obj = JSON.parse(data.responseText)[appID].data;
				if(obj != undefined) // undefined = doesn't have store page or doesn't exist
				// get steam apps categories : achievement, trading cards, etc
				{
					if(GameData[appID].last_checked === 0)
					{
						if (obj.type != "game")
						{
							GameData[appID].game = obj.fullgame.appid;
							tagCard.setAttribute("href", linkCard+obj.fullgame.appid);
							tagAchievement.setAttribute("href", linkAchievement+obj.fullgame.appid+"/achievements/");
						}
						else if(packID != "0" && PackageData[packID].games.indexOf(appID) == -1)
							PackageData[packID].games.push(appID);
					}

					var categories = obj.categories;
					if(categories != undefined)
					{
						var catCards = $.grep(categories, function(e){ return e.id == "29"; });
						if(catCards.length > 0)
						{
							if(cbCards) displayElems(tagCard);
							GameData[appID].cards = true;
							if(packID != "0")
							{
								PackageData[packID].cards = true;
								if(PackageData[packID].games.length > 1)
									tagCard.setAttribute("href", "http://ruphine.esy.es/steamgifts/TradingCard.php?packageid="+packID);
								else
									tagCard.setAttribute("href", linkCard+PackageData[packID].games[0]);
							}
						}

						var catAchievement = $.grep(categories, function(e){ return e.id == "22"; });
						if(catAchievement.length > 0)
						{
							if(cbAchievement) displayElems(tagAchievement);
							GameData[appID].achievement = true;
							if(packID != "0")
							{
								PackageData[packID].achievement = true;
								if(PackageData[packID].games.length > 1)
									tagAchievement.setAttribute("href", "http://ruphine.esy.es/steamgifts/Achievement.php?packageid="+packID);
								else
									tagAchievement.setAttribute("href", linkAchievement+PackageData[packID].games[0]+"/achievements/");
							}
						}
					}

					// get steam apps platforms: linux: boolean, mac: boolean
					var platforms = obj.platforms;
					if(platforms.linux)
					{
						if(cbLinux) displayElems(tagLinux);
						GameData[appID].linux = true;
						if(packID != "0") PackageData[packID].linux = true;
					}
					if(platforms.mac)
					{
						if(cbMac) displayElems(tagMac);
						GameData[appID].mac = true;
						if(packID != "0") PackageData[packID].mac = true;
					}

					// get steam apps genres
					if(obj.genres != undefined)
					{
						var genEarly = $.grep(obj.genres, function(e){ return e.id == "70"; });
						if(genEarly.length > 0)
						{
							if(cbEarly) displayElems(tagEarly);
							GameData[appID].early_access = true;
							if(packID != "0") PackageData[packID].early_access = true;
						}
						else
						{
							GameData[appID].early_access = false;
							if(packID != "0") PackageData[packID].early_access = false;
						}
					}
				}
				GameData[appID].last_checked = Date.now();
				GM_setValue("GameData", JSON.stringify(GameData));

				if(packID != "0")
				{
					PackageData[packID].last_checked = Date.now();
					GM_setValue("PackageData", JSON.stringify(PackageData));
				}
			},
			ontimeout: function(data)
			{
				console.log("[SG Game Tags] Request " + linkGameAPI+appID + " Timeout");
			}
		});
	}
}

function getBundleStatus(appID, type, tag)
{
	var Game = $.grep(arrBundled, function(e){ return (e.AppID == appID && e.Type == type); });
	if(Game.length > 0) //game found in bundle list
	{
		displayElems(tag);
		tag.setAttribute("title", TitleBundle+Game[0].BundledDate);
	}
}

function getBundleList()
{
	// console.log("[SG Game Tags] requesting " + linkBundleAPI);
	GM_xmlhttpRequest({
		method: "GET",
		timeout: 10000,
		url: linkBundleAPI,
		onload: function(data)
		{
			BundledGames = data.responseText;
			GM_setValue("BundledGames", BundledGames);

			BundledCache = Date.now();
			GM_setValue("BundledCache", BundledCache);
 			if(cbWishlist && UserdataCache < Date.now() - 6*60*60*1000) //6 hours
 				getUserdata();
 			else
				main();
		},
		ontimeout: function(data)
		{
			console.log("[SG Game Tags] Request " + linkBundleAPI + " Timeout");
			if(cbWishlist && UserdataCache < Date.now() - 6*60*60*1000) //6 hours
 				getUserdata();
 			else
				main();
		}
	});
}

function getUserdata()
{
	// console.log("[SG Game Tags] requesting " + linkUserAPI);
	GM_xmlhttpRequest({
		method: "GET",
		timeout: 10000,
		url: linkUserAPI,
		onload: function(data)
		{
			var result = JSON.parse(data.responseText);
			if(result.rgOwnedApps.length !== 0) //check if user logged in
			{
				UserdataAPI = data.responseText;
				GM_setValue("UserdataAPI", UserdataAPI);

				UserdataCache = Date.now();
				GM_setValue("UserdataCache", UserdataCache);
			}
			else
				console.log("[SG Game Tags] Unable to get wishlist data. User is not logged in to steam");

			main();
		},
		ontimeout: function(data)
		{
			console.log("[SG Game Tags] Request " + linkUserAPI + " Timeout");
			main();
		}
	});
}

function getHiddenStatus(appID, appName, elems)
{
	if(cbHidden)
	{
		// console.log("[SG Game Tags] requesting " + linkHidden+appName);
		$.get(linkHidden+appName, function(data)
		{
			var gamesfound = $(data).find("a.table__column__secondary-link");
			for(i=0; i<$(gamesfound).length; i++)
			{
				var url = $(gamesfound)[i].href;
				var ID = getAppIDfromLink(url);
				if(appID == ID)
				{
					//TODO : Save appID + true ke local cache
					displayElems(elems);
					return true; //exit function
				}
			}
		});
	}
}

function getWishlistStatus(appID, elems)
{
	appID = parseInt(appID);
	if(rgWishlist.indexOf(appID) >= 0)
		displayElems(elems);
}

function getSteamCategoriesFromPackage(packID, tagCard, tagAchievement, tagLinux, tagMac, tagEarly)
{
	var needRequest = false;
	if(PackageData[packID] == undefined || PackageData[packID].games == undefined)
	{
		var template = {"cards": false, "achievement": false, "mac": false, "linux": false, "early_access": false, "last_checked": 0, "games":[]};
		PackageData[packID] = template;
		needRequest = true;
	}
	else
	{
		var data = PackageData[packID];
		if(cbCards && data.cards)
		{
			displayElems(tagCard);
			if(data.games.length > 1)
			{
				tagCard.setAttribute("href", "http://ruphine.esy.es/steamgifts/TradingCard.php?packageid="+packID);
				tagCard.setAttribute("title", "There is " + data.games.length + " games in this package, and at least one of them have trading cards");
			}
			else
				tagCard.setAttribute("href", linkCard+data.games[0]);
		}
		if(cbAchievement && data.achievement)
		{
			displayElems(tagAchievement);
			if(data.games.length > 1)
			{
				tagAchievement.setAttribute("href", "http://ruphine.esy.es/steamgifts/Achievement.php?packageid="+packID);
				tagAchievement.setAttribute("title", "There is " + data.games.length + " games in this package, and at least one of them have achievements");
			}
			else
				tagAchievement.setAttribute("href", linkAchievement+data.games[0]+"/achievements/");
		}
		if(cbMac && data.mac) displayElems(tagMac);
		if(cbLinux && data.linux) displayElems(tagLinux);
		if(cbEarly && data.early_access) displayElems(tagEarly);

		if(data.last_checked < (Date.now() - (24 * 60 * 60 * 1000))) // 24 hours have passed since last checked
		{
			if((!data.cards && cbCards) || (!data.achievement && cbAchievement) || (!data.mac && cbMac ) || (!data.linux && cbLinux) || (data.early_access && cbEarly))
				needRequest = true;
		}
	}
	if(needRequest)
	{
		// console.log("[SG Game Tags] requesting " + linkPackAPI+packID);
		GM_xmlhttpRequest({
			method: "GET",
			timeout: 10000,
			url: linkPackAPI+packID,
			onload: function(data)
			{
				var IDs = JSON.parse(data.responseText)[packID].data;
				if(IDs == undefined)
				{
					PackageData[packID].cards = false;
					PackageData[packID].achievement = false;
					PackageData[packID].mac = false;
					PackageData[packID].linux = false;
					PackageData[packID].early_access = false;
					PackageData[packID].games = [];
					PackageData[packID].last_checked = Date.now();
					GM_setValue("PackageData", JSON.stringify(PackageData));
				}
				else
				{
					IDs = IDs.apps;
					$.each(IDs, function(index)
					{
						getSteamCategories(IDs[index].id, tagCard, tagAchievement, tagLinux, tagMac, tagEarly, packID);
					});
				}
			},
			ontimeout: function(data)
			{
				console.log("[SG Game Tags] Request " + linkPackAPI+packID + " Timeout");
			}
		});
	}
}

function PrepareJSON()
{
	var template = {"cards": false, "achievement": false, "mac": false, "linux": false, "early_access": false, "last_checked": 0};
	var a = {"0":template};
	var temp = JSON.stringify(a);
	GameData = JSON.parse(temp);
	PackageData = JSON.parse(temp);
	GM_setValue("GameData", JSON.stringify(GameData));
	GM_setValue("PackageData", JSON.stringify(PackageData));
}

function getAppIDfromLink(link)
{
	var url = link.split("/");
	return url[url.length-2];
}

function isApp(link)
{
	var pattern = /\/app|apps\/0-9\//;
	return pattern.test(link);
}

function isPackage(link)
{
	var pattern = /\/sub|subs\/0-9\//;
	return pattern.test(link);
}


// ========================================== create new giveaway page ========================================================
function NewGiveawayDivUpdated(event)
{
	if(event.type == "DOMNodeInserted") //show bundle tag for shown game
	{
		var gamesfound = $(".table__row-inner-wrap");
		$(".tags").remove();
		$(".table__row-inner-wrap").each(function(index, element)
		{
			var url = $(element).find("a.table__column__secondary-link").text();
			var ID = getAppIDfromLink(url);
			var Name = $(element).find(".table__column__heading").text();
			var Target = $(element).find(".table__column--width-fill");

			$(".js__autocomplete-data").off("DOMNodeInserted");
			var tagBundle = createTag(ClassBundle, TitleBundle, TextBundle, linkBundle+Name, Target);
			$(tagBundle).css("float", "right");

			var type = isApp(url) ? 'app' : 'sub';
			getBundleStatus(ID, type, tagBundle);
		});
		if(gamesfound.length > 0)
		{
			$(".js__autocomplete-data").on("DOMNodeRemoved", NewGiveawayDivUpdated);

			$(".table__row-inner-wrap").on("click", function(event)
			{
				var url = $(this).find("a.table__column__secondary-link").text();
				var ID = getAppIDfromLink(url);
				var Name = $(this).find(".table__column__heading").text();
				var Target = $(".js__autocomplete-name")[0];
				tagBundle = createTag(ClassBundle, TitleBundle, TextBundle, linkBundle+Name, Target);
				var type = isApp(url) ? 'app' : 'sub';
				getBundleStatus(ID, type, tagBundle);
			});
		}
	}
	else if(event.type == "DOMNodeRemoved")//show / remove tag of selected game
	{
		$(".js__autocomplete-data").off("DOMNodeRemoved");
		$(".table__row-inner-wrap").off("click");
		$(".js__autocomplete-data").on("DOMNodeInserted", NewGiveawayDivUpdated);
	}
}

// ========================================== setting page ========================================================
function initSetting()
{
	var no = $(".form__heading").length + 1;
	initTagOnOffSetting(no);
	initTagPositionSetting(no+1);
	initTagColorSetting(no+2);
}

function initTagOnOffSetting(no)
{
	var CheckIcon = '<i class="form__checkbox__default fa fa-circle-o"></i><i class="form__checkbox__hover fa fa-circle"></i><i class="form__checkbox__selected fa fa-check-circle"></i>';
	var form__row = document.createElement("div");
	form__row.setAttribute("class", "form__row");

		var form__heading = document.createElement("div");
		form__heading.setAttribute("class", "form__heading");

			var form__heading__number = document.createElement("div");
			form__heading__number.setAttribute("class", "form__heading__number");
			form__heading__number.innerHTML = no + ".";

			var form__heading__text = document.createElement("div");
			form__heading__text.setAttribute("class", "form__heading__text");
			form__heading__text.setAttribute("title", "If you have performance issues, try disable tags you don't need");
			form__heading__text.innerHTML = "[SG Game Tags] Which tags do you want to see?";

		$(form__heading).append(form__heading__number).append(form__heading__text);

		var form__row__indent = document.createElement("div");
		form__row__indent.setAttribute("class", "form__row__indent");

			var form__checkbox_1 = createCheckBox("my__checkbox", CheckIcon + "Trading Cards", cbCards);
			var form__checkbox_2 = createCheckBox("my__checkbox", CheckIcon + "Achievements", cbAchievement);
			var form__checkbox_3 = createCheckBox("my__checkbox", CheckIcon + "Bundled", cbBundled);
			var form__checkbox_4 = createCheckBox("my__checkbox", CheckIcon + "Hidden", cbHidden);
			var form__checkbox_5 = createCheckBox("my__checkbox", CheckIcon + "Wishlist", cbWishlist);
			var form__checkbox_6 = createCheckBox("my__checkbox", CheckIcon + "Linux", cbLinux);
			var form__checkbox_7 = createCheckBox("my__checkbox", CheckIcon + "Mac", cbMac);
			var form__checkbox_8 = createCheckBox("my__checkbox", CheckIcon + "Early Access", cbEarly);

			$(form__checkbox_1).click(function(){toggleCBTags(form__checkbox_1, "cbCards");});
			$(form__checkbox_2).click(function(){toggleCBTags(form__checkbox_2, "cbAchievement");});
			$(form__checkbox_3).click(function(){toggleCBTags(form__checkbox_3, "cbBundled");});
			$(form__checkbox_4).click(function(){toggleCBTags(form__checkbox_4, "cbHidden");});
			$(form__checkbox_5).click(function(){toggleCBTags(form__checkbox_5, "cbWishlist");});
			$(form__checkbox_6).click(function(){toggleCBTags(form__checkbox_6, "cbLinux");});
			$(form__checkbox_7).click(function(){toggleCBTags(form__checkbox_7, "cbMac");});
			$(form__checkbox_8).click(function(){toggleCBTags(form__checkbox_8, "cbEarly");});

		$(form__row__indent)
			.append(form__checkbox_1)
			.append(form__checkbox_2)
			.append(form__checkbox_3)
			.append(form__checkbox_4)
			.append(form__checkbox_5)
			.append(form__checkbox_6)
			.append(form__checkbox_7)
			.append(form__checkbox_8);

	$(form__row).append(form__heading).append(form__row__indent).insertBefore(".js__submit-form");

	var desc = document.createElement("div");
	desc.setAttribute("class", "form__input-description");
	desc.innerHTML = "No need to press Save Changes button. It is automatically saved when the value changed.";
	$(desc).appendTo([form__row__indent]);

	changeCBColor();
}

function initTagPositionSetting(no)
{
	var CheckIcon = '<i class="form__checkbox__default fa fa-circle-o"></i><i class="form__checkbox__hover fa fa-circle"></i><i class="form__checkbox__selected fa fa-check-circle"></i>';
	var form__row = document.createElement("div");
	form__row.setAttribute("class", "form__row");

		var form__heading = document.createElement("div");
		form__heading.setAttribute("class", "form__heading");

			var form__heading__number = document.createElement("div");
			form__heading__number.setAttribute("class", "form__heading__number");
			form__heading__number.innerHTML = no + ".";

			var form__heading__text = document.createElement("div");
			form__heading__text.setAttribute("class", "form__heading__text");
			form__heading__text.setAttribute("title", "This setting doesn't affect performance, only visual change.");
			form__heading__text.innerHTML = "[SG Game Tags] Tags Style";

		$(form__heading).append(form__heading__number).append(form__heading__text);

		var form__row__indent = document.createElement("div");
		form__row__indent.setAttribute("class", "form__row__indent");
			var form__checkbox_1 = createCheckBox("form__checkbox", CheckIcon + "(Original) Full Text tags below game title", cbTagStyle == 1);
			var form__checkbox_2 = createCheckBox("form__checkbox", CheckIcon + "(Minimalist) One letter tags beside game title", cbTagStyle == 2);

			form__checkbox_1.setAttribute("title", 'The tags will display "Trading Cards", "Bundled", etc. This option will increase page height.');
			form__checkbox_2.setAttribute("title", 'The tags will just display first letter. "Trading Cards" becomes "T", "Bundled" becomes "B", etc.');

			$(form__checkbox_1).click(
				function()
				{
					$(form__checkbox_2).removeClass("is-selected").addClass("is-disabled");
					$(form__checkbox_1).removeClass("is-disabled").addClass("is-selected");
					GM_setValue("cbTagStyle", 1);

					$(".tags-bundle").text(TextBundle);
					$(".tags-card").text(TextCard);
					$(".tags-achievement").text(TextAchievement);
					$(".tags-wishlist").text(TextWishlist);
					$(".tags-linux").text(TextLinux);
					$(".tags-mac").text(TextMac);
					$(".tags-early").text(TextEarly);
					$(".tags-hidden").text(TextHidden);
				}
			);
			$(form__checkbox_2).click(
				function()
				{
					$(form__checkbox_1).removeClass("is-selected").addClass("is-disabled");
					$(form__checkbox_2).removeClass("is-disabled").addClass("is-selected");
					GM_setValue("cbTagStyle", 2);
					$(".row .tags").each(function(index, element)
					{
						$(element).text($(element).text().substring(0,1));
					});
				}
			);

		$(form__row__indent).append(form__checkbox_1).append(form__checkbox_2);

	$(form__row).append(form__heading).append(form__row__indent).insertBefore(".js__submit-form");

	var desc = document.createElement("div");
	desc.setAttribute("class", "form__input-description");
	desc.innerHTML = "No need to press Save Changes button. It is automatically saved when the value changed.";
	$(desc).appendTo([form__row__indent]);
}

function createCheckBox(_class, _html, cbValue)
{
	var cb = document.createElement("div");
	cb.setAttribute("class", _class);
	cb.innerHTML = _html;
	if(cbValue)
		$(cb).addClass("is-selected");
	else
		$(cb).addClass("is-disabled");

	return cb;
}

function toggleCBTags(cbElems, cbName)
{
	var cbValue;
	if(cbName == "cbCards")
	{
		cbCards = !cbCards;
		cbValue = cbCards;
	}
	else if(cbName == "cbAchievement")
	{
		cbAchievement = !cbAchievement;
		cbValue = cbAchievement;
	}
	else if(cbName == "cbBundled")
	{
		cbBundled = !cbBundled;
		cbValue = cbBundled;
	}
	else if(cbName == "cbHidden")
	{
		cbHidden = !cbHidden;
		cbValue = cbHidden;
	}
	else if(cbName == "cbWishlist")
	{
		cbWishlist = !cbWishlist;
		cbValue = cbWishlist;
	}
	else if(cbName == "cbLinux")
	{
		cbLinux = !cbLinux;
		cbValue = cbLinux;
	}
	else if(cbName == "cbMac")
	{
		cbMac = !cbMac;
		cbValue = cbMac;
	}
	else if(cbName == "cbEarly")
	{
		cbEarly = !cbEarly;
		cbValue = cbEarly;
	}

	GM_setValue(cbName, cbValue);
	if(cbValue)
		$(cbElems).removeClass("is-disabled").addClass("is-selected");
	else
		$(cbElems).removeClass("is-selected").addClass("is-disabled");

	changeCBColor();
}

function changeCBColor()
{
	var colorCBDisabled = $(".form__checkbox.is-disabled").css("color");
	var colorCBSelected = $(".form__checkbox.is-selected").css("color");

	$(".my__checkbox.is-disabled").css("color", colorCBDisabled);
	$(".my__checkbox.is-selected").css("color", colorCBSelected);
}

function initTagColorSetting(no)
{
	var require = ' \
		<style type="text/css"> \
			.row div { display: inline-block; } \
			.preview-tags { width: 80px; margin-left: 10px; } \
			.row .markdown {margin-left: 10px; cursor: pointer; }\
		</style> \
		<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/spectrum/1.8.0/spectrum.min.css" /> \
	';

	$("head").append(require);

	var form__row = document.createElement("div");
	form__row.setAttribute("class", "form__row");

		var form__heading = document.createElement("div");
		form__heading.setAttribute("class", "form__heading");

			var form__heading__number = document.createElement("div");
			form__heading__number.setAttribute("class", "form__heading__number");
			form__heading__number.innerHTML = no + ".";

			var form__heading__text = document.createElement("div");
			form__heading__text.setAttribute("class", "form__heading__text");
			form__heading__text.innerHTML = "[SG Game Tags] Customized tags color";

		$(form__heading).append(form__heading__number).append(form__heading__text);

		var form__row__indent = document.createElement("div");
		form__row__indent.setAttribute("class", "form__row__indent");
		form__row__indent.innerHTML = ' \
			<div class="row"> \
				<input type="text" class="colorpicker" id="bundle-1"/> \
				<input type="text" class="colorpicker" id="bundle-2"/> \
				<div class="markdown"><a class="default_bundle">Default</a></div> \
				<div class="preview-tags"><a class="tags tags-bundle" style="display: inline-block;">Bundled</a></div> \
			</div> \
			<div class="row"> \
				<input type="text" class="colorpicker" id="card-1"/> \
				<input type="text" class="colorpicker" id="card-2"/> \
				<div class="markdown"><a class="default_card">Default</a></div> \
				<div class="preview-tags"><a class="tags tags-card" style="display: inline-block;">Trading Card</a></div> \
			</div> \
			<div class="row"> \
				<input type="text" class="colorpicker" id="achievement-1"/> \
				<input type="text" class="colorpicker" id="achievement-2"/> \
				<div class="markdown"><a class="default_achievement">Default</a></div> \
				<div class="preview-tags"><a class="tags tags-achievement" style="display: inline-block;">Achievements</a></div> \
			</div> \
			<div class="row"> \
				<input type="text" class="colorpicker" id="wishlist-1"/> \
				<input type="text" class="colorpicker" id="wishlist-2"/> \
				<div class="markdown"><a class="default_wishlist">Default</a></div> \
				<div class="preview-tags"><a class="tags tags-wishlist" style="display: inline-block;">Wishlist</a></div> \
			</div> \
			<div class="row"> \
				<input type="text" class="colorpicker" id="linux-1"/> \
				<input type="text" class="colorpicker" id="linux-2"/> \
				<div class="markdown"><a class="default_linux">Default</a></div> \
				<div class="preview-tags"><a class="tags tags-linux" style="display: inline-block;">Linux</a></div> \
			</div> \
			<div class="row"> \
				<input type="text" class="colorpicker" id="mac-1"/> \
				<input type="text" class="colorpicker" id="mac-2"/> \
				<div class="markdown"><a class="default_mac">Default</a></div> \
				<div class="preview-tags"><a class="tags tags-mac" style="display: inline-block;">Mac</a></div> \
			</div> \
			<div class="row"> \
				<input type="text" class="colorpicker" id="early-1"/> \
				<input type="text" class="colorpicker" id="early-2"/> \
				<div class="markdown"><a class="default_early">Default</a></div> \
				<div class="preview-tags"><a class="tags tags-early" style="display: inline-block;">Early Access</a></div> \
			</div> \
			<div class="row"> \
				<input type="text" class="colorpicker" id="hidden-1"/> \
				<input type="text" class="colorpicker" id="hidden-2"/> \
				<div class="markdown"><a class="default_hidden">Default</a></div> \
				<div class="preview-tags"><a class="tags tags-hidden" style="display: inline-block;">Hidden</a></div> \
			</div> \
		';

	$(form__row).append(form__heading).append(form__row__indent).insertBefore(".js__submit-form");

	var desc = document.createElement("div");
	desc.setAttribute("class", "form__input-description");
	desc.innerHTML = "No need to press Save Changes button. It is automatically saved when colorpicker closed.";
	$(desc).appendTo([form__row__indent]);

	if(cbTagStyle == 2) // change tags if minimalist selected
	{
		$(".row .tags").each(function(index, element)
		{
			$(element).text($(element).text().substring(0,1));
		});
	}

	initColorpicker("bundle-1", GM_getValue("bundle-1", default_bundle), "tags-bundle", "background-color");
	initColorpicker("bundle-2", GM_getValue("bundle-2", "#FFFFFF"), "tags-bundle", "color");
	initColorpicker("card-1", GM_getValue("card-1", default_card), "tags-card", "background-color");
	initColorpicker("card-2", GM_getValue("card-2", "#FFFFFF"), "tags-card", "color");
	initColorpicker("achievement-1", GM_getValue("achievement-1", default_achievement), "tags-achievement", "background-color");
	initColorpicker("achievement-2", GM_getValue("achievement-2", "#FFFFFF"), "tags-achievement", "color");
	initColorpicker("wishlist-1", GM_getValue("wishlist-1", default_wishlist), "tags-wishlist", "background-color");
	initColorpicker("wishlist-2", GM_getValue("wishlist-2", "#FFFFFF"), "tags-wishlist", "color");
	initColorpicker("linux-1", GM_getValue("linux-1", default_linux), "tags-linux", "background-color");
	initColorpicker("linux-2", GM_getValue("linux-2", "#FFFFFF"), "tags-linux", "color");
	initColorpicker("mac-1", GM_getValue("mac-1", default_mac), "tags-mac", "background-color");
	initColorpicker("mac-2", GM_getValue("mac-2", "#FFFFFF"), "tags-mac", "color");
	initColorpicker("early-1", GM_getValue("early-1", default_early), "tags-early", "background-color");
	initColorpicker("early-2", GM_getValue("early-2", "#FFFFFF"), "tags-early", "color");
	initColorpicker("hidden-1", GM_getValue("hidden-1", default_hidden), "tags-hidden", "background-color");
	initColorpicker("hidden-2", GM_getValue("hidden-2", "#FFFFFF"), "tags-hidden", "color");

	$(".default_bundle").click(function(){clickDefaultColor("bundle", default_bundle);});
	$(".default_card").click(function(){clickDefaultColor("card", default_card);});
	$(".default_achievement").click(function(){clickDefaultColor("achievement", default_achievement);});
	$(".default_wishlist").click(function(){clickDefaultColor("wishlist", default_wishlist);});
	$(".default_linux").click(function(){clickDefaultColor("linux", default_linux);});
	$(".default_mac").click(function(){clickDefaultColor("mac", default_mac);});
	$(".default_early").click(function(){clickDefaultColor("early", default_early);});
	$(".default_hidden").click(function(){clickDefaultColor("hidden", default_hidden);});
}

function initColorpicker(id, currentColor, tag, property)
{
	$("#"+id).spectrum({
		showInput: true, // show color code and lets user input color code
		showInitial: true, //show previous color to compare with new color
		showPalette: true,
		showSelectionPalette: true,
		preferredFormat: "hex", //display hex code
		localStorageKey: "spectrum.sggametags",
		maxSelectionSize: 8,
		palette: [
			[default_bundle, default_card, default_achievement, default_wishlist, default_linux, default_mac, default_early, default_hidden],
			["#000","#444","#666","#999","#ccc","#eee","#f3f3f3","#fff"],
			["#f00","#f90","#ff0","#0f0","#0ff","#00f","#90f","#f0f"],
			["#f4cccc","#fce5cd","#fff2cc","#d9ead3","#d0e0e3","#cfe2f3","#d9d2e9","#ead1dc"],
			["#ea9999","#f9cb9c","#ffe599","#b6d7a8","#a2c4c9","#9fc5e8","#b4a7d6","#d5a6bd"],
			["#e06666","#f6b26b","#ffd966","#93c47d","#76a5af","#6fa8dc","#8e7cc3","#c27ba0"],
			["#c00","#e69138","#f1c232","#6aa84f","#45818e","#3d85c6","#674ea7","#a64d79"],
			["#900","#b45f06","#bf9000","#38761d","#134f5c","#0b5394","#351c75","#741b47"],
			["#600","#783f04","#7f6000","#274e13","#0c343d","#073763","#20124d","#4c1130"]
		],
		color:currentColor,
		move: function(color){ $("."+tag).css(property, color.toHexString()); },
		change: function(color){ $("."+tag).css(property, color.toHexString()); },
		hide: function(color){
			GM_setValue(id, color.toHexString());
		}
	});
}

function clickDefaultColor(name, defaultvalue)
{
	console.log("clicked");
	GM_setValue(name+"-1", defaultvalue);
	GM_setValue(name+"-2", "#FFFFFF");
	$(".tags-"+name).css("background-color", defaultvalue).css("color", "#FFFFFF");
	$("#"+name+"-1").spectrum("set", defaultvalue);
	$("#"+name+"-2").spectrum("set", "#FFFFFF");
}

// ==================================================================================================
function AddShortcutToSettingPage()
{
	var shortcut = '\
		<a class="nav__row sggt_shortcut" href="/account/settings/giveaways"> \
			<i class="icon-yellow fa fa-fw fa-tag"></i> \
			<div class="nav__row__summary"> \
				<p class="nav__row__summary__name">SG Game Tags Setting</p> \
				<p class="nav__row__summary__description">Open SG Game Tags setting page</span>.</p> \
			</div> \
		</a> \
	';
	var dropdown = $(".nav__right-container .nav__absolute-dropdown .nav__row");
	$(dropdown[2]).before(shortcut); // just before logout button
}