TW Friends

Friend Management for The West Events

Pada tanggal 27 Maret 2017. Lihat %(latest_version_link).

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               TW Friends
// @name:el            TW Friends
// @name:es            TW Friends
// @name:fr            TW Friends
// @name:pl            TW Friends
// @name:pt-PT         TW Friends
// @version            0.28
// @license            LGPLv3
// @description        Friend Management for The West Events
// @description:el     Διαχείριση Φίλων για τις Εκδηλώσεις του The West
// @description:es     Gestión de Amigos para Eventos de The West
// @description:fr     Gestion des Amis pour les Evénements de The West
// @description:pl     Menadżer zarządzania Przyjaciółmi podczas eventów The West
// @description:pt-PT  Gestão de amigos para eventos no The West
// @author             hiroaki
// @translation        pepe100 (es_ES)
// @translation        jccwest (pt_PT)
// @translation        Bartosz86 (pl_PL)
// @include            http*://*.the-west.*/game.php*
// @include            http*://*.tw.innogames.*/game.php*
// @grant              none
// @namespace          https://greasyfork.org/users/3197
// @icon               https://cdn.rawgit.com/TWFriends/scripts/master/friends.png
// ==/UserScript==

function hiroFriendsScript(fn) {
	var script = document.createElement('script');
	script.setAttribute("type", "application/javascript");
	script.textContent = '(' + fn + ')();';
	document.body.appendChild(script);
	document.body.removeChild(script);
}
hiroFriendsScript(function() {
	var VERSION = 0.28;
	var installURL = "https://greasyfork.org/scripts/2992-tw-friends";
	var codeURL = "https://greasyfork.org/scripts/2992-tw-friends/code/TW%20Friends.user.js";
	var versionURL = "https://gist.githubusercontent.com/TWFriends/974718d615afe3d2c2a2/raw/version?"+Date.now();
	var scriptName = "TW Friends";
	var scriptAuthor = "hiroaki";
	var refreshMs = 2 * 60 * 1e3;	// 2 minutes
	var enableLog = true;
	var enableInv = true;
	var enableVersionCheck = false;
	HiroFriends = {
		api: TheWestApi.register('HiroFriends', scriptName, '2.04', Game.version.toString(), scriptAuthor, installURL),
		version: VERSION,
		latestVersion: undefined,
		storageItem: "HiroFriends.version",
		cdnBase: '',
		eventCurrencyImage : '',
		eventName : '',
		eventInfo : {},
		eventEndStamp : 0,
		friends : {},
		interval: false,
		locale: 'en_US',
		pendingInvitations: 0,
		messages: {
			el_GR: {
				description: '<h1>Διαχείριση Φίλων για τις Εκδηλώσεις του The West</h1><p style="margin: 8pt;">Μην κλικάρετε πολύ γρήγορα, για να αποφύγετε ένα σερί κακής τύχης :)</p><p style="margin: 8pt;">Υποστηριζόμενες Εκδηλώσεις:</p><ul style="list-style: disc outside; margin-left: 16pt; padding-left: 16pt;"><li>Άγιος Βαλεντίνος</li><li>Πάσχα</li><li>Οκτόμπερφεστ</li><li>Ημέρα Ανεξαρτησίας</li><li>Ημέρα των Νεκρών</li></ul><p style="margin: 8pt;"><a target="_blank" href="https://forum.the-west.gr/showthread.php?t=8029">Συζήτηση στο Φόρουμ</a> | <a target="_blank" href="https://greasyfork.org/scripts/2992-tw-friends/feedback">Feedback</a>: Αναφορά Bugs, Ιδέες, Μεταφράσεις.</p><p style="margin: 8pt;"><b>Ευχαριστίες</b>: jccwest, noolas, pepe100, Pnevma, Bartosz86</p>',
				version: 'έκδοση',
				upgrade: 'Μια νέα έκδοση είναι διαθέσιμη. Θες να την εγκαταστήσεις;',
				refresh: 'Ανανέωση',
				timeLeft: 'Χρόνος που υπολείπεται μέχρι το τέλος της εκδήλωσης',
				serverTime: 'ώρα διακομιστή',
				availFriends: 'Αριθμός Φιλων στους οποίους μπορείς να στείλεις τώρα',
				totalFriends: 'Αριθμός Φίλων',
				pendingInvitation: 'Μία εκκρεμής πρόσκληση',
				pendingInvitations: 'εκκρεμείς πρoσκλήσεις',
				noFriends: 'Δεν έχεις φίλους',
				name: 'Όνομα',
				received: 'Έλαβες',
				frequency: 'Συχνότητα',
				removeFriend: 'Αφαίρεση φίλου',
				removeConfirm: 'Θέλεις πραγματικά να διαγράψεις αυτόν τον παίχτη από τη λίστα;',
				removeSuccess: 'Ο φίλος αφαιρέθηκε από τη λίστα.',
				removeFailed: 'Ο φίλος δεν μπόρεσε να αφαιρεθεί',
				exporter: 'Εξαγωγή',
				everything: 'Όλα',
				stats: 'Στατιστικά',
				since: 'από',
				collected: 'Έχεις συλλέξει',
				friends: 'Φίλοι',
				jobs: 'Εργασίες',
				fortBattles: 'Μάχες Οχυρών',
				adventures: 'Περιπέτειες',
				duels: 'Μονομαχίες',
				npcDuels: 'Μονομαχίες με NPC',
				construction: 'Χτίσιμο',
				quests: 'Αποστολές',
				itemUse: 'Χρήση Αντικειμένων',
				other: 'Άλλα',
				used: 'Έχεις χρησιμοποιήσει',
				timerReset: 'Μηδενισμός χρονομέτρου',
				bribe: 'Δωροδοκίες',
				inventory: 'Αποθέματα',
				nextYear: 'Του χρόνου',
				theEnd: 'Τετέλεσται',
			},
			en_US: {
				description: '<h1>Friend Management for The West Events</h1><p style="margin: 8pt;">Don&#039;t click too fast, to avoid a streak of bad luck upon you :)</p><p style="margin: 8pt;">Supported Events:</p><ul style="list-style: disc outside; margin-left: 16pt; padding-left: 16pt;"><li>Valentine&#039;s Day</li><li>Easter</li><li>Independence Day</li><li>Oktoberfest</li><li>Day of the Dead</li></ul><p style="margin: 8pt;"><a target="_blank" href="https://greasyfork.org/scripts/2992-tw-friends/feedback">Feedback</a>: Bug Reports, Ideas, Translations (in English).</p><p style="margin: 8pt;"><b>Credits</b>: jccwest, noolas, pepe100, Pnevma, Bartosz86</p>',
				version: 'version',
				upgrade: 'A new version is available. Do you want to upgrade now?',
				refresh: 'Refresh',
				timeLeft: 'Time Left until the event ends',
				serverTime: 'server time',
				availFriends: 'Number of Friends you can send now',
				totalFriends: 'Number of Friends',
				pendingInvitation: 'One pending invitation',
				pendingInvitations: 'pending invitations',
				noFriends: 'No Friends',
				name: 'Name',
				received: 'Received',
				frequency: 'Frequency',
				removeFriend: 'Remove friend',
				removeConfirm: 'Do you really want to delete this player from the list?',
				removeSuccess: 'Friend removed from your list.',
				removeFailed: 'Friend could not be removed',
				exporter: 'Export',
				everything: 'Everything',
				stats: 'Stats',
				since: 'since',
				collected: 'Collected',
				friends: 'Friends',
				jobs: 'Jobs',
				fortBattles: 'Fort Battles',
				adventures: 'Adventures',
				duels: 'Duels',
				npcDuels: 'NPC Duels',
				construction: 'Construction',
				quests: 'Quests',
				itemUse: 'Item Use',
				other: 'Other',
				used: 'Used',
				timerReset: 'Reset Timers',
				bribe: 'Bribe',
				gameAction: 'Event game action',
				inventory: 'Inventory',
				nextYear: 'Next Year',
				theEnd: 'The End',
			},
			es_ES: {
				description: '<h1>Gestión de Amigos para Eventos de The West</h1><p style="margin: 8pt;">No haga clic demasiado rápido, para evitar una racha de mala suerte :)</p><p style="margin: 8pt;">Eventos soportados:</p><ul style="list-style: disc outside; margin-left: 16pt; padding-left: 16pt;"><li>Día de San Valentín</li><li>Pascua</li><li>Día de la Independencia</li><li>Oktoberfest</li><li>Día de los Muertos</li></ul><p style="margin: 8pt;"><a target="_blank" href="https://greasyfork.org/scripts/2992-tw-friends/feedback">Comentarios</a>: Bug Reports, Ideas, Translations (in English).</p><p style="margin: 8pt;"><b>Créditos</b>: jccwest, noolas, pepe100, Pnevma, Bartosz86</p>',
				version: 'versión',
				upgrade: 'Una nueva versión está disponible. ¿Quieres instalarla ahora?',
				refresh: 'Actualizar',
				timeLeft: 'Tiempo que queda hasta el final del evento',
				serverTime: 'hora del servidor',
				availFriends: 'Número de Amigos que se puede enviar ahora',
				totalFriends: 'Número de Amigos',
				pendingInvitation: 'Una invitación pendiente',
				pendingInvitations: 'invitaciones pendientes',
				noFriends: 'Sin Amigos',
				name: 'Nombre',
				received: 'Recibidos',
				frequency: 'Frecuencia',
				removeFriend: 'Remover amigo',
				removeConfirm: '¿Estas seguro que quieres eliminar a este jugador de tu lista?',
				removeSuccess: 'Amigo removido de tu lista',
				removeFailed: 'El amigo no ha podido ser eliminado',
				exporter: 'Exportar',
				everything: 'Todo',
				stats: 'Estadísticas',
				since: 'desde',
				collected: 'Conseguidos',
				friends: 'Amigos',
				jobs: 'Trabajos',
				fortBattles: 'Batallas de fuertes',
				adventures: 'Aventuras',
				duels: 'Duelos',
				npcDuels: 'Duelos NPC',
				construction: 'Ampliación',
				quests: 'Busquedas',
				itemUse: 'Objetos usados',
				other: 'Otros',
				used: 'Usados',
				timerReset: 'Reiniciar temporizadores',
				bribe: 'Soborno',
				inventory: 'Inventario',
				nextYear: 'El próximo año',
				theEnd: 'Final',
			},
			fr_FR: {
				description: '<h1>Gestion des Amis pour les Evénements de The West</h1><p style="margin: 8pt;">Ne cliquez pas trop vite, pour éviter une série de malchance sur vous :)</p><p style="margin: 8pt;">Evénements:</p><ul style="list-style: disc outside; margin-left: 16pt; padding-left: 16pt;"><li>Saint Valentin</li><li>Pâques</li><li>Jour De L&#039;Indépendance</li><li>Oktoberfest</li><li>Jour des Morts</li></ul><p style="margin: 8pt;"><a target="_blank" href="https://greasyfork.org/scripts/2992-tw-friends/feedback">Commentaires</a>: Bug Reports, Ideas, Translations (in English).</p><p style="margin: 8pt;"><b>Credits</b>: jccwest, noolas, pepe100, Pnevma, Bartosz86</p>',
				version: 'version',
				upgrade: 'Une nouvelle version est disponible. Installer maintenant?',
				refresh: 'Rafraîchir',
				timeLeft: "Temps restant jusqu'à la fin de l'événement",
				serverTime: 'horaire du serveur',
				availFriends: 'Nombre des amis que tu peux envoyer maintenant',
				totalFriends: 'Nombre des Amis',
				pendingInvitation: 'Une invitation en attente',
				pendingInvitations: 'invitations en attentes',
				noFriends: "Pas d'Amis",
				name: 'Nom',
				received: 'Tu as reçu',
				frequency: 'Fréquence',
				removeFriend: "Supprimer l'ami(e)",
				removeConfirm: 'Veux-tu vraiment supprimer ce joueur de la liste?',
				removeSuccess: 'Ami supprimé de la liste',
				removeFailed: "L'ami n'a pas pu être supprimé",
				exporter: 'Exportation',
				everything: 'Tout',
				stats: 'Statistiques',
				since: 'à partir de',
				collected: 'Collectés',
				friends: 'Amis',
				jobs: 'Travaux',
				fortBattles: 'Batailles',
				adventures: 'Aventures',
				duels: 'Duels',
				npcDuels: 'Duels PNJ',
				construction: 'Agrandissement',
				quests: 'Quêtes',
				itemUse: 'Objets utilisés',
				other: 'Autres',
				used: 'Utilisés',
				timerReset: 'Se battre en duel',
				bribe: 'Soudoyer',
				inventory: 'Inventaire',
				nextYear: "L'année prochaine",
				theEnd: 'La Fin',
			},
			pl_PL: {
				description: '<h1>Menadżer zarządzania Przyjaciółmi podczas eventów The West</h1><p style="margin: 8pt;">Nie klikaj zbyt szybko, bo to będzie bardzo niemiłe dla Ciebie :)</p><p style="margin: 8pt;">Obsługiwane Eventy:</p><ul style="list-style: disc outside; margin-left: 16pt; padding-left: 16pt;"><li>Walentynki</li><li>Wielkanoc</li><li>Dzień Niepodległości</li><li>Oktoberfest</li><li>Dia de los Muertos</li></ul><p style="margin: 8pt;"><a target="_blank" href="https://forum.the-west.pl/showthread.php?t=60424">Forum</a> | <a target="_blank" href="https://greasyfork.org/scripts/2992-tw-friends/feedback">Opinie</a>: Zgłaszanie błędów, pomysłów, propozycji zmian (w języku angielskim).</p><p style="margin: 8pt;"><b>Uznanie dla</b>: jccwest, noolas, pepe100, Pnevma, Bartosz86</p>',
				version: 'wersja',
				upgrade: 'Dostępna jest nowa wersja. Czy chcesz dokonać aktualizacji?',
				refresh: 'Odśwież',
				timeLeft: 'Czas pozostały do zakończenia eventu',
				serverTime: 'czasu serwera',
				availFriends: 'Liczba przyjaciół do których można wysłać prezent',
				totalFriends: 'Łączna liczba przyjaciół',
				pendingInvitation: 'jedno oczekujące zaproszenie',
				pendingInvitations: 'oczekujących zaproszeń',
				noFriends: 'Brak przyjaciół',
				name: 'Nazwa gracza',
				received: 'Otrzymano',
				frequency: 'Częstotliwość',
				removeFriend: 'Usuń przyjaciela',
				removeConfirm: 'Czy na pewno chcesz usunąć tego gracza z listy przyjaciół?',
				removeSuccess: 'Przyjaciel usunięty z Twojej listy.',
				removeFailed: 'Nie można usunąć przyjaciela',
				exporter: 'Eksport',
				everything: 'Wszystko',
				stats: 'Statystyka',
				since: 'od',
				collected: 'Uzbierano',
				friends: 'Znajomi',
				jobs: 'Prace',
				fortBattles: 'Bitwy fortowe',
				adventures: 'Przygody',
				duels: 'Pojedynki',
				npcDuels: 'Pojedynki z NPC',
				construction: 'Rozbudowa',
				quests: 'Zadania',
				itemUse: 'Użycie przedmiotów',
				other: 'Inne',
				used: 'Użyto',
				timerReset: 'Zresetuj liczniki',
				bribe: 'Przekup',
				gameAction: 'Akcje podczas eventu',
				inventory: 'Ekwipunek',
				nextYear: 'W przyszłym roku',
				theEnd: 'Koniec',
			},
			pt_PT: {
				description: '<h1>Gestão de amigos para eventos no The West</h1><p style="margin: 8pt;">não clique rápido demais, para evitar uma maré de azar :)</p><p style="margin: 8pt;">Eventos suportados:</p><ul style="list-style: disc outside; margin-left: 16pt; padding-left: 16pt;"><li>Dia dos namorados</li><li>Páscoa</li><li>Dia da independência</li><li>Oktoberfest</li><li>Dia de los Muertos</li></ul><p style="margin: 8pt;"><a target="_blank" href="https://greasyfork.org/scripts/2992-tw-friends/feedback">Comentários</a>: Bug Reports, Ideas, Translations (in English).</p><p style="margin: 8pt;"><b>Créditos</b>: jccwest, noolas, pepe100, Pnevma, Bartosz86</p>',
				version: 'versão',
				upgrade: 'A nova versão está disponível. Atualize agora?',
				refresh: 'Actualizar',
				timeLeft: 'Tempo restante até ao final do evento',
				serverTime: 'hora do servidor',
				availFriends: 'Número de amigos a quem pode enviar agora',
				totalFriends: 'Número de Amigos',
				pendingInvitation: 'Um convite pendente',
				pendingInvitations: 'convites pendentes',
				noFriends: 'Sem Amigos',
				name: 'Nome',
				received: 'Recebidos',
				frequency: 'Freqüência',
				removeFriend: 'Remover amigo',
				removeConfirm: 'Queres mesmo remover amigo da lista ?',
				removeSuccess: 'Amigo removido da lista',
				removeFailed: 'Amigo não pode ser removido',
				exporter: 'Exportar',
				everything: 'Tudo',
				stats: 'Estatísticas',
				since: 'desde',
				collected: 'Recolhidos',
				friends: 'Amigos',
				jobs: 'Trabalhos',
				fortBattles: 'Batalhas de Forte',
				adventures: 'Aventuras MPI',
				duels: 'Duelos',
				npcDuels: 'Duelos NPC',
				construction: 'Construção',
				quests: 'Aventuras',
				itemUse: 'Itens usados',
				other: 'Outros',
				used: 'Usados',
				timerReset: 'Reiniciar temporizadores',
				bribe: 'Suborno',
				inventory: 'Inventário',
				nextYear: 'Próximo Ano',
				theEnd: 'Final',
			},
		},
		timeLeft : 0,
		total : 0,
		avail: 0,
		log: { firstLog: Date.now()/1e3, lastLog: 0, newLastLog: 0, friendLog: {}, entries: [], count_friends: 0, count_job: 0, count_duel: 0, count_npc: 0, count_fort: 0, count_mpi: 0, count_quest: 0, count_build: 0, count_item: 0, count_other: 0, count_reset: 0, count_bribe: 0, count_action: 0, times_reset: 0, times_bribe: 0, received: 0, used: 0 },
		spanCounter: $("<span />", { id: "hiro_friends_counter", style: "position: absolute; right: 5px; color: #f8c57c; font-size: 13pt; height: 25px; line-height: 25px; bottom: 0px" }),
		spanInvitations: null,
		spanTimeLeft: null,
		divFriendsAvail: null,
		imgFriendsAvail: null,
		/* Inno buildDateObject() function currently buggy */
		buildDateObject: function(timeStr, isServerTime) {
			var regEx, match, d = new Date(0), today = new Date();
			regEx = /^(?:(3[01]|[012]?[0-9]|\*)\.(?:(1[012]|0?[1-9]|\*)\.((?:19|20)?\d\d|\*)))?(?: ?(2[0-3]|[01]?\d|\*)\:([0-5]?\d|\*)(?:\:([0-5]?\d|\*))?)?$/;
			if(match = timeStr.match(regEx)) {
				d.setFullYear(match[3] !== undefined ? (match[3] == '*' ? today.getFullYear() : parseInt(match[3], 10)) : today.getFullYear());
				d.setMonth(match[2] !== undefined ? (match[2] == '*' ? today.getMonth() : parseInt(match[2], 10) - 1) : today.getMonth());
				d.setDate(match[1] !== undefined ? (match[1] == '*' ? today.getDate() : parseInt(match[1], 10)) : today.getDate());
				d.setHours(match[4] !== undefined ? (match[4] == '*' ? today.getHours() : match[4]) : 0);
				d.setMinutes(match[5] !== undefined ? (match[5] == '*' ? today.getMinutes() : match[5]) : 0);
				d.setSeconds(match[6] !== undefined ? (match[6] == '*' ? today.getSeconds() : match[6]) : 0);
				d.setMilliseconds(0);
			}
			if(isServerTime) d = new Date(d - Game.serverTimeDifference);
			return d;
		},
		buildTimeStamp: function (timeStr, isServerTime) {
			return this.buildDateObject(timeStr, isServerTime).getTime();
		},
		items: {
			Hearts: {
				2557000 : 1250,		/* Small Heart Bag - 1250 hearts */
				2558000 : 2500,		/* Large Heart Bag - 2500 hearts */
				2561000 : 100,		/* Love Apple - 100 hearts */
				2562000 : 500,		/* Sugar hearts - 500 hearts */
				2563000 : 650,		/* 650 hearts */
				2564000 : 1500,		/* 1500 hearts */
				2565000 : 3250,		/* 3250 hearts */
				2566000 : 9000,		/* 9000 hearts */
				2567000 : 16000,	/* 16000 hearts */
			},
			Easter: {
				2698000 : 2500,		/* Efficient Easter egg container - 2500 eggs */
				2590000 : 650,		/* 650 Eggs */
				2591000 : 1500,		/* 1500 Eggs */
				2592000 : 3250,		/* 3250 Eggs */
				2593000 : 9000,		/* 9000 Eggs */
				2594000 : 16000,	/* 16000 Eggs */
			},
			Independence: {
				2619000 : 650,
				2620000 : 1500,
				2621000 : 3250,
				2622000 : 9000,
				2623000 : 16000,
			},
			Octoberfest: {
				371000 : 650,
				973000 : 1500,
				974000 : 3250,
				975000 : 9000,
				976000 : 16000,
			},
			DayOfDead: {
				2665000 : 1250,		/* Flower pot - 1250 Cempasúchil flowers */
				2666000 : 2500,		/* Big Flower pot - 2500 Cempasúchil flowers */
				2675000 : 25,		/* Cempasúchil Case - 25 Cempasúchil flowers */
				2676000 : 650,
				2677000 : 1500,
				2678000 : 3250,
				2679000 : 9000,
				2680000 : 16000,
			}
		},
		eventItems: {
			divInventory: null,
			imgInventory: null,
			imgTitle: '',
			Inventory: [],
			Available: [],
			total: 0,
			coolDownComplete: false,
			check: function () {
				HiroFriends.eventItems.Inventory = [];
				HiroFriends.eventItems.Available = [];
				HiroFriends.eventItems.total = 0;
				HiroFriends.eventItems.coolDownComplete = false;
				if(undefined === HiroFriends.items[HiroFriends.eventName]) return false;
				var now = new ServerDate().getTime()/1e3, coolDown, invItem;
				$.each(HiroFriends.items[HiroFriends.eventName], function(itemId, amount) {
					invItem = Bag.getItemByItemId(itemId);
					if(invItem) {
						HiroFriends.eventItems.Inventory.push(invItem);
						coolDown = Bag.itemCooldown[itemId];
						if(!coolDown) {
							HiroFriends.eventItems.Available.push(invItem);
							HiroFriends.eventItems.total += invItem.count*amount;
						}
						else if(coolDown <= now) {
							HiroFriends.eventItems.coolDownComplete = true;
							HiroFriends.eventItems.Available.push(invItem);
							HiroFriends.eventItems.total += amount;
						}
					}
				});
				HiroFriends.eventItems.imgInventory.attr('src', HiroFriends.eventItems.coolDownComplete ? HiroFriends.cdnBase+'/images/icons/clock.png' : HiroFriends.eventCurrencyImage);
				HiroFriends.eventItems.imgInventory.attr('title', HiroFriends.eventItems.total ? HiroFriends.eventItems.total+' <img src="' + HiroFriends.eventCurrencyImage + '" alt="">' : '');
				return true;
			},
			display: function() {
				HiroFriends.eventItems.check();
				if(HiroFriends.eventItems.Inventory.length > 0) {
					if(!Bag.loaded) {
						EventHandler.listen('inventory_loaded', function () {
							Wear.open();
							Inventory.showSearchResult(HiroFriends.eventItems.Inventory);
							return EventHandler.ONE_TIME_EVENT;
						});
						Bag.loadItems();
					}
					else {
						Wear.open();
						Inventory.showSearchResult(HiroFriends.eventItems.Inventory);
					}
				}
				HiroFriends.eventItems.divInventory.hide();
			},
		},
		display: function(sort) {
			var friend_time, server_time = Game.getServerTime();
			var maindiv = $('<div class="hiro_friends_maindiv" />');
			var friends = [];
			for(var key in this.friends) if(this.friends.hasOwnProperty(key)) friends.push({ id: key, name: this.friends[key].name, activation_time: this.friends[key].activation_time, recv: this.friends[key].recv });
			if(!friends.length) $('<h1 style="text-align: center; color: #990000; margin-bottom: 80px;">'+this.localeMsg('noFriends')+'</h1>').appendTo(maindiv);
			else {
				var hiroTable;
				switch(sort) {
					case "name" 	:	friends.sort(this.sortByName); break;
					case "name_desc":	friends.sort(this.sortByName).reverse(); break;
					case "recv" 	:	friends.sort(this.sortByRecv); break;
					case "recv_asc"	:	friends.sort(this.sortByRecv).reverse(); break;
					case "time_asc"	:	friends.sort(this.sortByTime).reverse(); break;
					case "time"	:
					default		:	sort = "time"; friends.sort(this.sortByTime);
				}
				var thName = $('<a style="cursor: pointer;"><img src="'+this.cdnBase+'/images/icons/user.png" alt="" />&nbsp;'+this.localeMsg('name')+'</a>').click(function(){ HiroFriends.display(sort == 'name' ? 'name_desc' : 'name'); return false; });
				var thAction = $('<a style="cursor: pointer;"><img src="'+this.cdnBase+'/images/icons/clock.png" alt="" />&nbsp;'+this.eventInfo.label+'</a>').click(function(){ HiroFriends.display(sort == 'time' ? 'time_asc' : 'time'); return false; });
				var thRecv = enableLog ? $('<a style="cursor: pointer;" title="'+this.localeMsg('since')+' '+new Date(this.log.firstLog*1e3).toDateTimeString()+'"><img src="'+this.cdnBase+'/images/icons/watch.png" alt="" />&nbsp;'+this.localeMsg('received')+'</a>').click(function(){ HiroFriends.display(sort == 'recv' ? 'recv_asc' : 'recv'); return false; }) : '';
				hiroTable = new west.gui.Table().appendTo(maindiv).addColumn("hf_idx").addColumn("hf_player").addColumn("hf_action").addColumn("hf_log").addColumn("hf_delete").appendToCell("head", "hf_idx", '&nbsp;').appendToCell("head", "hf_player", thName).appendToCell("head", "hf_action", thAction).appendToCell("head","hf_log",thRecv).appendToCell("head", "hf_delete", '&nbsp;');
				var idx = 1;
				var now = Date.now()/1e3;
				$.each(friends, function(key, val) {
					var actionCell, recvCell;
					friend_time = val.activation_time + HiroFriends.eventInfo.cooldown - server_time;
					if(friend_time > HiroFriends.timeLeft) actionCell = '('+HiroFriends.localeMsg('nextYear')+')';
					else if(friend_time > 0) actionCell = '('+friend_time.formatDurationBuffWay()+')';
					else {
						actionCell = $('<a style="cursor: pointer;">'+HiroFriends.eventInfo.label+'</a>').click({ id: val.id, ev: HiroFriends.eventName }, function(e) {
							$(this).parent().parent().remove();
							Ajax.remoteCall("friendsbar", "event", { player_id: val.id, event: HiroFriends.eventName }, function(response) {
								if(response.error) return MessageError(response.msg).show();
								MessageSuccess(response.msg).show();
								HiroFriends.friends[val.id].activation_time = Date.now()/1e3;
								if(HiroFriends.avail) -- HiroFriends.avail;
								HiroFriends.updateCounter();
								if(WestUi.FriendsBar.friendsBarUi !== null)
									WestUi.FriendsBar.friendsBarUi.friendsBar.eventActivations[val.id][HiroFriends.eventName] = response.activationTime;
							});
							return false;
						});
					}
					recvCell = '';
					if(enableLog) {
						if(val.recv) {
							var recv_list = '';
							HiroFriends.log.friendLog[val.id].dates.sort(function(a, b){ return new Date(a)-new Date(b); });
							if(HiroFriends.log.friendLog[val.id].total && HiroFriends.log.friendLog[val.id].dates.length > 1) {
								recv_list += '<p style=&quot;text-align: center; margin-bottom: 8px;&quot;>'+HiroFriends.localeMsg('frequency')+': <b>'+((now - HiroFriends.log.friendLog[val.id].dates[0]) / (HiroFriends.log.friendLog[val.id].dates.length-1)).formatDuration()+'</b></p>';
							}
							recv_list += '<ol style=&quot;list-style-type: decimal; padding: 0 0 0 20px;&quot;>';
							$.each(HiroFriends.log.friendLog[val.id].dates, function(dkey, dval) {
								recv_list += '<li style=&quot;display: list-item; white-space: nowrap;&quot;>' + new Date(dval * 1e3).toDateTimeStringNice() + '</li>';
							});
							recv_list += '<ol>';
							recvCell = '<span title="'+recv_list+'" style="cursor: help;">'+val.recv+'</span>';
						}
						else recvCell = val.recv;
					}
					hiroTable.appendRow(null, 'hiroFriendRow_'+val.id)
						.appendToCell(-1, "hf_idx", idx)
						.appendToCell(-1, "hf_player", '<a href="javascript:void(PlayerProfileWindow.open('+val.id+'));">' + val.name + '</a>')
						.appendToCell(-1, "hf_action", actionCell)
						.appendToCell(-1, "hf_log", recvCell)
						.appendToCell(-1, "hf_delete", '<a href="javascript:void(HiroFriends.removeFriend('+val.id+'));"><img style="width:16px; height: 16px;" title="'+HiroFriends.localeMsg('removeFriend')+'" src="'+HiroFriends.cdnBase+'/images/icons/delete.png" alt="delete" /></a>');
					++ idx;
				});
				hiroTable.appendToCell('foot', 'hf_idx', '<a target="_blank" href="'+installURL+'"><img src="'+this.cdnBase+'/images/icons/link.png" alt=""></a>');
				hiroTable.appendToCell('foot', 'hf_player', '<a target="_blank" href="'+installURL+'">'+scriptName+'</a> '+this.localeMsg('version')+' ' + this.version.toFixed(2));
				if('https://gr1.the-west.gr' == Game.gameURL || 'https://gr4.the-west.gr' == Game.gameURL || 'https://gr5.the-west.gr' == Game.gameURL) hiroTable.appendToCell('foot', 'hf_action', 'by <a href="javascript:void(PlayerProfileWindow.open(92184));">'+scriptAuthor+'</a>');
				else if('https://zz1.beta.the-west.net' == Game.gameURL) hiroTable.appendToCell('foot', 'hf_action', 'by <a href="javascript:void(PlayerProfileWindow.open(16866));">'+scriptAuthor+'</a>');
				else hiroTable.appendToCell('foot', 'hf_action', 'by '+scriptAuthor);
				if(this.pendingInvitations) hiroTable.appendToCell('foot', 'hf_delete', '<a href="javascript:void(FriendslistWindow.open(\'openrequests\'));"><img style="width:16px; height: 16px;" title="'+this.pendingInvitationsMsg()+'" src="'+this.cdnBase+'/images/icons/friends.png" alt="add" /></a>');
				if(enableLog) hiroTable.appendToCell('foot', 'hf_log', $('<a style="cursor: pointer;">'+HiroFriends.localeMsg('exporter')+'</a>').click(function() {
					HiroFriends.log.entries.sort(function(a,b) { return a.date - b.date; });
					var tsv_friends = "id\t"+HiroFriends.localeMsg('name')+"\t"+HiroFriends.localeMsg('received')+"\r\n";
					$.each(HiroFriends.log.friendLog, function(key,val) { tsv_friends += key+"\t"+val.name+"\t"+val.total+"\r\n"; });
					new west.gui.Dialog(HiroFriends.localeMsg('exporter'),'<b>'+HiroFriends.localeMsg('friends')+'</b> (<a download="TW Friends - '+HiroFriends.eventName+' - '+ Game.worldName+' - '+Character.name+' - '+HiroFriends.localeMsg('friends')+' - '+Date.now()+'.tsv" href="data:text/tab-separated-values,'+encodeURI(tsv_friends)+'">TSV</a>):<br /><textarea cols="60" rows="8" style="width: 100%; height: 100px;">' + JSON.stringify(HiroFriends.log.friendLog) + '</textarea><br /><b>'+HiroFriends.localeMsg('everything')+'</b>:<br /><textarea cols="60" rows="8" style="width: 100%; height: 100px;">' + JSON.stringify(HiroFriends.log.entries) + '</textarea>').setModal(true,true,{bg:HiroFriends.cdnBase+'/images/curtain_bg.png',opacity:0.7}).addButton("ok").show();
					return false;
				}) );
			}
			if(enableLog || this.eventItems.total) {
				var statsTable = '<table style="margin: auto; width: 96%;">';
				if(enableLog) statsTable += '<tr><th colspan="3" style="border-bottom: 1px dotted;">'+this.localeMsg('stats')+' ('+this.localeMsg('since')+' '+new Date(this.log.firstLog*1e3).toDateTimeString()+')</th></tr><tr style="vertical-align: top;"><td style="white-space: nowrap;">'+this.localeMsg('collected')+':</td><td style="color: #006600; font-weight: bold; text-align: right; padding-right: 8pt;">'+this.log.received+'</td><td> <span style="white-space: nowrap;">'+this.localeMsg('friends')+': <b>'+this.log.count_friends+'</b>,</span> <span style="white-space: nowrap;">'+this.localeMsg('jobs')+': <b>'+this.log.count_job+'</b>,</span> <span style="white-space: nowrap;">'+this.localeMsg('fortBattles')+': <b>'+this.log.count_fort+'</b>,</span> <span style="white-space: nowrap;">'+this.localeMsg('adventures')+': <b>'+this.log.count_mpi+'</b>,</span> <span style="white-space: nowrap;">'+this.localeMsg('duels')+': <b>'+this.log.count_duel+'</b>,</span> <span style="white-space: nowrap;">'+this.localeMsg('npcDuels')+': <b>'+this.log.count_npc+'</b>,</span> <span style="white-space: nowrap;">'+this.localeMsg('construction')+': <b>'+this.log.count_build+'</b>,</span> <span style="white-space: nowrap;">'+this.localeMsg('quests')+': <b>'+this.log.count_quest+'</b>,</span> <span style="white-space: nowrap;">'+this.localeMsg('itemUse')+': <b>'+this.log.count_item+'</b>,</span> <span style="white-space: nowrap;">'+this.localeMsg('other')+': <b>'+this.log.count_other+'</b></span></td></tr>'+(this.log.used?'<tr style="vertical-align: top;"><td style="white-space: nowrap;">'+this.localeMsg('used')+':</td><td style="color: #660000; font-weight: bold; text-align: right; padding-right: 8pt;">'+this.log.used+'</td><td>'+(this.log.count_reset?'<span style="white-space: nowrap;">'+this.localeMsg('timerReset')+': <b>'+this.log.count_reset+'</b> (#'+this.log.times_reset+'),</span> ' : '')+(this.log.count_action?'<span style="white-space: nowrap;">'+this.localeMsg('gameAction')+': <b>'+this.log.count_action+'</b>,</span> ' : '')+(this.log.count_bribe?'<span style="white-space: nowrap;">'+this.localeMsg('bribe')+': <b>'+this.log.count_bribe+'</b> (#'+this.log.times_bribe+')</span>' : '')+'</td></tr>' : '');
				if(this.eventItems.total) statsTable += '<tr><td style="white-space: nowrap;">'+this.localeMsg('inventory')+':</td><td style="text-align: right; padding-right: 8pt;"><a href="javascript:void(HiroFriends.eventItems.display());">'+this.eventItems.total+'</a></td><td>&nbsp;</td></tr>';
				statsTable += '</table>';
				$(statsTable).appendTo(maindiv);
			}
			var hiroPane = new west.gui.Scrollpane();
			hiroPane.appendContent(maindiv);
			var hiroWindow = wman.open("HiroFriends_"+this.eventName, null, "noreload").setMiniTitle(this.eventInfo.label).setTitle(this.eventInfo.label).appendToContentPane(hiroPane.getMainDiv());
		},
		eventManager: function(eventName) {
			if(undefined === Game.sesData[eventName] || undefined === Game.sesData[eventName].friendsbar) return false;
			this.eventName = eventName;
			this.eventInfo = Game.sesData[eventName].friendsbar;
			if(undefined === Game.sesData[this.eventName].meta.end) return false;
			this.eventEndStamp = (this.buildTimeStamp(Game.sesData[this.eventName].meta.end) - Game.serverTimeDifference) / 1e3;
			this.timeLeft = this.eventEndStamp - Game.getServerTime();
			if(this.timeLeft < 0) return false;
			this.cdnBase = (undefined === Game.cdnURL) ? "https://westzzs.innogamescdn.com" : Game.cdnURL;
			$.when(this.getLog()).done(function() {
				HiroFriends.design();
			});
		},
		design: function() {
			HiroFriends.spanTimeLeft = $("<span />", { id: "hiro_event_timeleft", style: "position: absolute; left: 5px; color: #d3d3d3; font-size: 11px; height: 25px; line-height: 25px; cursor: pointer", title: HiroFriends.localeMsg('timeLeft')+'<br />('+new Date(HiroFriends.buildTimeStamp(Game.sesData[HiroFriends.eventName].meta.end)).toDateTimeStringNice()+' '+HiroFriends.localeMsg('serverTime')+')' });
			var eventImage = HiroFriends.cdnBase + "/images/interface/friendsbar/events/" + HiroFriends.eventName + ".png";	// event based
			if(HiroFriends.eventName == 'Octoberfest') eventImage = HiroFriends.cdnBase + "/images/window/events/octoberfest/pretzels_icon.png";
			var divContainer = $("<div />", { id: "hiro_friends_container", style: "position: absolute; top: 32px; right: 50%; margin-right: 120px; z-index: 16; width: 180px; height: 36px; text-align: left; text-shadow: 1px 1px 1px #000; background: url('"+HiroFriends.cdnBase+"/images/interface/custom_unit_counter_sprite.png?2') no-repeat scroll 50% 0px transparent;" })
			var divCounter = $("<div />", { id: "hiro_friends", style: "background: url('"+HiroFriends.cdnBase+"/images/interface/custom_unit_counter_sprite.png?2') no-repeat scroll 0 -36px rgba(0, 0, 0, 0); height: 25px; left: 32px; line-height: 25px; padding: 0 5px; position: absolute; top: 3px; width: 105px; z-index: 1; text-shadow: 1px 1px 1px #000;" });
			var divRefresh = $("<div />", { style: "width: 24px; height: 24px; position: absolute; left: 8px; top: 3px; z-index: 3; padding: 4px 0px 0px 4px;" });
			var spanRefresh = $('<span />', { title: HiroFriends.localeMsg('refresh'), style: "display: inline-block; width: 20px; height: 20px; cursor: pointer; background: url('"+HiroFriends.cdnBase+"/images/tw2gui/window/window2_buttons.png?5') repeat scroll 0px -20px transparent;" });
			var spanSend = $("<span />", { style: "width: 26px; height: 26px; left: auto; position: absolute; right: 7px; top: 2px; z-index: 3;" });
			var imageSend = $("<img />", { src: eventImage, title: HiroFriends.eventInfo.label, style: "width: 26px; height: 26px; cursor: pointer" });
			if(HiroFriends.pendingInvitations) {
				HiroFriends.spanCounter.css("right", "20px");
				HiroFriends.spanInvitations = $("<span />", { id: "hiro_friends_invitations", title: HiroFriends.pendingInvitationsMsg(), style: "position: absolute; right: 0px; width: 19px; height: 25px; background-image: url('"+HiroFriends.cdnBase+"/images/interface/more.jpg'); background-repeat: no-repeat;" });
				HiroFriends.spanInvitations.hover(function() { $(this).css("background-position", "0px -25px"); }, function() { $(this).css("background-position", ""); });
				HiroFriends.spanInvitations.click(function() { $(this).hide(); HiroFriends.spanCounter.css("right", "5px"); FriendslistWindow.open('openrequests'); return false; });
				divCounter.append(HiroFriends.spanInvitations);
			}
			divContainer.append(divRefresh.append(spanRefresh), spanSend.append(imageSend), divCounter.append(HiroFriends.spanTimeLeft, HiroFriends.spanCounter)).appendTo("#user-interface");
			spanRefresh.hover(function() { $(this).css("background-position", ""); }, function() { $(this).css("background-position", "0px -20px"); });
			spanRefresh.click(function() { HiroFriends.spanCounter.slideUp(500, function() { HiroFriends.fetch(); }).slideDown(1500); return false; });
			imageSend.click(function() { HiroFriends.open(); return false; });
			HiroFriends.eventCurrencyImage = "/images/icons/"+HiroFriends.eventName+".png";
			if(enableInv) {
				HiroFriends.eventItems.divInventory = $("<div />", { id: "hiro_friends_inventory_container", style: "position: absolute; top: 0px; right: 0px; z-index: 18; width: 20px; height: 18px;" }).hide().appendTo("#ui_bottombar .ui_bottombar_wrapper .button:first .dock-image");
				HiroFriends.eventItems.imgInventory = $('<img src="'+HiroFriends.eventCurrencyImage+'" alt="" title="">').click(function(e){ e.preventDefault(); HiroFriends.eventItems.display(); return false; }).appendTo(HiroFriends.eventItems.divInventory);
			}
			HiroFriends.divFriendsAvail = $("<div />", { id: "hiro_friends_bottombar_friends", style: "position: absolute; top: 0px; right: 0px; z-index: 18; width: 20px; height: 18px;" }).hide().appendTo("#ui_bottombar .ui_bottombar_wrapper .button:nth-child(3) .dock-image");
			HiroFriends.imgFriendsAvail = $('<img src="'+HiroFriends.eventCurrencyImage+'" alt="" title="'+scriptName+'">').click(function(e){ e.preventDefault(); HiroFriends.open(); return false; }).appendTo(HiroFriends.divFriendsAvail);
			HiroFriends.updateTimer();
			if(typeof(Storage) !== "undefined") {
				var previousVersion = (localStorage.getItem(HiroFriends.storageItem) === null) ? 0 : parseFloat(localStorage.getItem(HiroFriends.storageItem));
				localStorage.setItem(HiroFriends.storageItem, HiroFriends.version);
				// if(previousVersion && HiroFriends.version > previousVersion) var msg=new west.gui.Dialog("TW Friends", "Script upgraded to version "+HiroFriends.version, west.gui.Dialog.SYS_WARNING).addButton("OK").show();
			}
			$("<style>.hf_idx { width: 32px; text-align: right; padding-right: 8px; } .hf_player { width: 250px; } .hf_action { width: 200px; } .hf_log { width: 100px; text-align: right; padding-right: 8px; } .hf_delete { width: 40px; text-align: center; } div.tbody .hf_idx, div.tbody .hf_delete { background-image: url('"+HiroFriends.cdnBase+"/images/tw2gui/table/cell_shadow_y.png'); }</style>").appendTo("head");
			HiroFriends.fetch();
		},
		fetch: function() {
			if(this.interval !== false) clearInterval(this.interval);
			var event_times = {};
			var friends = {}, total = 0, avail = 0, recv = 0;
			var server_time = Game.getServerTime(), activation_time, friend_time;
			if(this.timeLeft < 0) {
				$("#hiro_friends_container").slideUp(5000);
				if(enableInv) HiroFriends.eventItems.divInventory.hide(5000);
				throw "Event is over";
			}
			if(enableInv) {
				if(HiroFriends.eventItems.check() && HiroFriends.eventItems.Available.length > 0)
					HiroFriends.eventItems.divInventory.show(5000);
				else HiroFriends.eventItems.divInventory.hide();
			}
			return $.post("/game.php?window=friendsbar&mode=search", { search_type: "friends" } , function(data) {
				$.each(data.eventActivations, function(key, val) {
					if(val.event_name == HiroFriends.eventName) event_times[val.friend_id] = val.activation_time;
				});
				$.each(data.players, function(key, val) {
					if(val.name !== Character.name) {
						activation_time = (event_times[val.player_id] !== undefined) ? event_times[val.player_id]: 0;
						if(undefined === HiroFriends.log.friendLog[val.player_id]) {
							recv = 0;
							HiroFriends.log.friendLog[val.player_id] = { name: val.name, total: 0, dates: [] };
						}
						else recv = HiroFriends.log.friendLog[val.player_id].total;
						friends[val.player_id] = { name: val.name, activation_time: activation_time, recv: recv };
						++ total;
						if(activation_time + HiroFriends.eventInfo.cooldown - server_time <= 0) ++ avail;
					}
				});
				HiroFriends.friends = friends;
				HiroFriends.avail = avail;
				HiroFriends.total = total;
				HiroFriends.updateCounter();
				if(HiroFriends.avail) {
					HiroFriends.imgFriendsAvail.attr('title', 'TW Friends: '+HiroFriends.avail+'/'+HiroFriends.total);
					HiroFriends.divFriendsAvail.show(5000);
				}
				else HiroFriends.divFriendsAvail.hide();
				HiroFriends.interval = setInterval(function() { HiroFriends.fetch(); }, refreshMs);
			});
		},
		getLogPage: function(page, limit, deferred) {
			return $.ajax({ type: "POST", url: "/game.php?window=ses&mode=log", data: { ses_id: HiroFriends.eventName, page: page, limit: limit }, success: function(data) {
				var details;
				var hasNext = data.hasNext;
				var count = 0;
				var limit = data.limit;
				page = data.page + 1;
				$.each(data.entries, function(key, val) {
					count = parseInt(val.value);
					if(val.date < HiroFriends.log.firstLog) HiroFriends.log.firstLog = val.date;
					if(val.date <= HiroFriends.log.lastLog) {
						hasNext = false;
						return false;
					}
					HiroFriends.log.entries.push(val);
					if(val.date > HiroFriends.log.newLastLog) {
						HiroFriends.log.newLastLog = val.date;
					}
					switch(val.type) {
						case "friendDrop":
							if(null !== val.details) {
								details = JSON.parse(val.details);
								if(undefined !== HiroFriends.friends[details.player_id]) HiroFriends.friends[details.player_id].recv += count;
								if(undefined === HiroFriends.log.friendLog[details.player_id]) HiroFriends.log.friendLog[details.player_id] = { name: details.name, total: count, dates: [] };
								else HiroFriends.log.friendLog[details.player_id].total += count;
								HiroFriends.log.friendLog[details.player_id].dates.push(val.date);
							}
							HiroFriends.log.count_friends += count;
							HiroFriends.log.received += count;
							break;
						case "jobDrop":		HiroFriends.log.count_job += count; HiroFriends.log.received += count; break;
						case "buildDrop":	HiroFriends.log.count_build += count; HiroFriends.log.received += count; break;
						case "duelDrop":	HiroFriends.log.count_duel += count; HiroFriends.log.received += count; break;
						case "duelNPCDrop":	HiroFriends.log.count_npc += count; HiroFriends.log.received += count; break;
						case "battleDrop":	HiroFriends.log.count_fort += count; HiroFriends.log.received += count; break;
						case "adventureDrop":	HiroFriends.log.count_mpi += count; HiroFriends.log.received += count; break;
						case "questDrop":	HiroFriends.log.count_quest += count; HiroFriends.log.received += count; break;
						case "itemUse":		HiroFriends.log.count_item += count; HiroFriends.log.received += count; break;
						case "wofPay":
							HiroFriends.log.used += count;
							if(null !== val.details) {
								if(val.details == "timerreset") {
									HiroFriends.log.count_reset += count;
									++ HiroFriends.log.times_reset;
								}
								else if(val.details == "sneakyshot") {
									HiroFriends.log.count_bribe += count;
									++ HiroFriends.log.times_bribe;
								}
							}
							break;
						default:
							HiroFriends.log.count_other += count;
							HiroFriends.log.received += count;
					}
				});
				if (hasNext) return HiroFriends.getLogPage(page, limit, deferred);
				else {
					/* Done */
					HiroFriends.log.lastLog = HiroFriends.log.newLastLog;
					Chat.Request.Nop();
					deferred.resolve();
				}
			} });
		},
		getLog: function() {
			if(enableLog) {
				var deferred = new $.Deferred();
				var limit = 100;
				HiroFriends.log.newLastLog = HiroFriends.log.lastLog;
				this.getLogPage(1, limit, deferred);
				return deferred.promise();
			}
		},
		getPendingInvitations: function() {
			return $.post("/game.php?window=character&mode=get_open_requests", function(data) {
				var openReq = 0;
				$.each(data.open_friends, function(key, val) { if(val.inviter_id != Character.playerId) ++ openReq; });
				HiroFriends.pendingInvitations = openReq;
			});
		},
		localeMsg: function(msg) {
			if(undefined !== this.messages[this.locale][msg]) return this.messages[this.locale][msg];
			if(undefined !== this.messages['en_US'][msg]) return this.messages['en_US'][msg];
			return '';
		},
		open: function() {
			if(!WestUi.FriendsBar.hidden) WestUi.FriendsBar.toggle();
			$.when(this.getLog()).done(function() {
				HiroFriends.fetch().done(function() {
					HiroFriends.divFriendsAvail.hide();
					HiroFriends.getPendingInvitations().done(function() {
						HiroFriends.display('time');
					});
				});
			});
		},
		pendingInvitationsMsg: function() { return this.pendingInvitations == 1 ? this.localeMsg('pendingInvitation') : this.pendingInvitations+' '+this.localeMsg('pendingInvitations'); },
		removeFriend: function(charId) {
			new west.gui.Dialog(HiroFriends.localeMsg('removeFriend'), HiroFriends.localeMsg('removeConfirm')).setIcon(west.gui.Dialog.SYS_QUESTION).addButton("yes", function() {
				Ajax.remoteCall('character', 'cancel_friendship', { friend_id: charId }, function(json) {
					if(json["result"]) {
						new UserMessage(HiroFriends.localeMsg('removeSuccess'), UserMessage.TYPE_SUCCESS).show();
						$("div.hiroFriendRow_" + charId).remove();
						$("div.friendData_" + charId, FriendslistWindow.DOM).remove();
						delete(HiroFriends.friends[charId]);
						if(HiroFriends.avail) -- HiroFriends.avail;
						if(HiroFriends.total) -- HiroFriends.total;
						HiroFriends.updateCounter();
						Chat.Friendslist.removeFriend(charId);
					}
					else new UserMessage(HiroFriends.localeMsg('removeFailed'), UserMessage.TYPE_ERROR).show();
				})
			}).addButton("no").show();
		},
		sortByName: function(a, b) { return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); },
		sortByRecv: function(a, b) { return b.recv - a.recv; },
		sortByTime: function(a, b) { return a.activation_time - b.activation_time; },
		updateCounter: function() {
			this.spanCounter.html('<span title="'+this.localeMsg('availFriends')+'">'+this.avail+'</span> <span style="color: #d3d3d3; font-size: 11px;" title="'+this.localeMsg('totalFriends')+'">/ '+this.total+'</span>');
		},
		updateTimer: function() {
			this.timeLeft = this.eventEndStamp - Game.getServerTime();
			if(this.timeLeft <= 0) {
				this.spanTimeLeft.html(this.localeMsg('theEnd'));
				this.fetch();
				return;
			}
			this.spanTimeLeft.html(this.timeLeft.formatDurationBuffWay());
			var seconds = 0;
			if(this.timeLeft < 70) seconds = 1;
			else if(this.timeLeft < 3660) seconds = 10;
			else if(this.timeLeft < 86520) seconds = 60;
			else seconds = 120;
			setTimeout(function() { HiroFriends.updateTimer(); }, seconds * 1e3);
		},
		scriptInit: function(tries, maxTries) {
			var ev, eventName;
			if(tries >= maxTries) return false;
			if(Game && Game.loaded && Character.playerId) {
				this.locale = (undefined === Game.locale || undefined == this.messages[Game.locale]) ? "en_US" : Game.locale;
				this.api.setGui(this.localeMsg('description'));
				if (enableVersionCheck) {
					try {
						$.getScript(versionURL).done(function() {
							if(HiroFriends.latestVersion && HiroFriends.latestVersion > VERSION) {
								var upgradeDialog = new west.gui.Dialog(scriptName, HiroFriends.localeMsg('upgrade'), west.gui.Dialog.SYS_WARNING).addButton('ok', function() {
									try { upgradeDialog.hide(); location.href = codeURL; } catch(e) {}
								}).addButton('cancel').show();
							}
						});
					}
					catch(e) { }
				}
				for(eventName in Game.sesData) {
					if(!Game.sesData.hasOwnProperty(eventName)) continue;
					var ev = Game.sesData[eventName];
					if(!ev.friendsbar) continue;
					if('Hearts' == eventName || 'Easter' == eventName || 'Independence' == eventName || 'DayOfDead' == eventName || 'Octoberfest' == eventName) {
						this.getPendingInvitations().done(function() {
							HiroFriends.eventManager(eventName);
						});
						return false;
					}
				}
				return true;
			}
			++ tries;
			setTimeout(function() { HiroFriends.scriptInit(tries, maxTries); }, tries * 1e3);
		},
	}
	try { HiroFriends.scriptInit(0, 100); } catch(e) { }
});