TW Friends

Friend Management for The West Events

当前为 2015-06-22 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name              TW Friends
// @name:el           TW Friends
// @name:es           TW Friends
// @name:fr           TW Friends
// @name:pt-PT        TW Friends
// @version           0.22
// @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:pt-PT Gestão de amigos para eventos no The West
// @author            hiroaki
// @translation       pepe100 (es_ES)
// @translation       jccwest (pt_PT)
// @include           http://*.the-west.*/game.php*
// @include           https://*.the-west.*/game.php*
// @include           http://*.tw.innogames.*/game.php*
// @include           https://*.tw.innogames.*/game.php*
// @grant             none
// @namespace         https://greasyfork.org/users/3197
// ==/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.22;
	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 = false;
	HiroFriends = {
		api: TheWestApi.register('HiroFriends', scriptName, '2.04', Game.version.toString(), scriptAuthor, installURL),
		version: VERSION,
		latestVersion: undefined,
		storageItem: "HiroFriends.version",
		cdnBase: '',
		eventName : '',
		eventInfo : {},
		eventEndStamp : 0,
		friend : {},
		friends : [],	/* to be removed in the next versions */
		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></ul><p style="margin: 8pt;"><a target="_blank" href="http://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</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: 'Χτίσιμο',
				itemUse: 'Χρήση Αντικειμένων',
				other: 'Άλλα',
				used: 'Έχεις χρησιμοποιήσει',
				timerReset: 'Μηδενισμός χρονομέτρου',
				bribe: 'Δωροδοκίες',
				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>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</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',
				itemUse: 'Item Use',
				other: 'Other',
				used: 'Used',
				timerReset: 'Reset Timers',
				bribe: 'Bribe',
				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>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</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',
				itemUse: 'Objetos usados',
				other: 'Otros',
				used: 'Usados',
				timerReset: 'Reiniciar temporizadores',
				bribe: 'Soborno',
				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>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</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',
				itemUse: 'Objets utilisés',
				other: 'Autres',
				used: 'Utilisés',
				timerReset: 'Se battre en duel',
				bribe: 'Soudoyer',
				nextYear: "L'année prochaine",
				theEnd: 'La Fin',
			},
			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>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</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',
				duels: 'Duelos',
				npcDuels: 'Duelos NPC',
				construction: 'Construção',
				itemUse: 'Itens usados',
				other: 'Outros',
				used: 'Usados',
				timerReset: 'Reiniciar temporizadores',
				bribe: 'Suborno',
				nextYear: 'Próximo Ano',
				theEnd: 'Final',
			},
		},
		timeLeft : 0,
		total : 0,
		avail: 0,
		log: { firstLog: Date.now()/1e3, lastLog: 0, friendLog: {}, entries: [], count_friends: 0, count_job: 0, count_duel: 0, count_npc: 0, count_fort: 0, count_mpi: 0, count_build: 0, count_item: 0, count_other: 0, count_reset: 0, count_bribe: 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,
		display: function(sort) {
			var friend_time, server_time = Game.getServerTime();
			var maindiv = $('<div class="hiro_friends_maindiv" />');
			var friends = [];
			for(var key in this.friend) if(this.friend.hasOwnProperty(key)) friends.push({ id: key, name: this.friend[key].name, activation_time: this.friend[key].activation_time, recv: this.friend[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 = $('<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; });
				thRecv = '';
				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.friend[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+'">TW Friends</a> '+this.localeMsg('version')+' ' + this.version.toFixed(2));
				if('http://gr1.the-west.gr' == Game.gameURL || 'http://gr4.the-west.gr' == Game.gameURL || 'http://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>');
				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) {
				var statsTable = $('<table style="margin: auto; width: 96%;"><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('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><span style="white-space: nowrap;">'+this.localeMsg('timerReset')+': <b>'+this.log.count_reset+'</b> (#'+this.log.times_reset+'),</span> <span style="white-space: nowrap;">'+this.localeMsg('bribe')+': <b>'+this.log.count_bribe+'</b> (#'+this.log.times_bribe+')</span></td></tr>' : '') + '</table>').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 = (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;
			if(enableLog) this.getLog();
			this.spanTimeLeft = $("<span />", { id: "hiro_event_timeleft", style: "position: absolute; left: 5px; color: #d3d3d3; font-size: 11px; height: 25px; line-height: 25px; cursor: pointer", title: this.localeMsg('timeLeft')+'<br />('+new Date(buildTimestamp(Game.sesData[this.eventName].meta.end)).toDateTimeStringNice()+' '+this.localeMsg('serverTime')+')' });
			var eventImage = this.cdnBase + "/images/interface/friendsbar/events/" + this.eventName + ".png";	// event based
			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('"+this.cdnBase+"/images/interface/custom_unit_counter_sprite.png?2') no-repeat scroll 50% 0px transparent;" })
			var divCounter = $("<div />", { id: "hiro_friends", style: "background: url('"+this.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: this.localeMsg('refresh'), style: "display: inline-block; width: 20px; height: 20px; cursor: pointer; background: url('"+this.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: this.eventInfo.label, style: "width: 26px; height: 26px; cursor: pointer" });
			if(this.pendingInvitations) {
				this.spanCounter.css("right", "20px");
				this.spanInvitations = $("<span />", { id: "hiro_friends_invitations", title: HiroFriends.pendingInvitationsMsg(), style: "position: absolute; right: 0px; width: 19px; height: 25px; background-image: url('"+this.cdnBase+"/images/interface/more.jpg'); background-repeat: no-repeat;" });
				this.spanInvitations.hover(function() { $(this).css("background-position", "0px -25px"); }, function() { $(this).css("background-position", ""); });
				this.spanInvitations.click(function() { $(this).hide(); HiroFriends.spanCounter.css("right", "5px"); FriendslistWindow.open('openrequests'); return false; });
				divCounter.append(this.spanInvitations);
			}
			divContainer.append(divRefresh.append(spanRefresh), spanSend.append(imageSend), divCounter.append(this.spanTimeLeft, this.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; });
			this.updateTimer();
			if(typeof(Storage) !== "undefined") {
				var previousVersion = (localStorage.getItem(this.storageItem) === null) ? 0 : parseFloat(localStorage.getItem(this.storageItem));
				localStorage.setItem(this.storageItem, this.version);
				// if(previousVersion && this.version > previousVersion) var msg=new west.gui.Dialog("TW Friends", "Script upgraded to version "+this.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('"+this.cdnBase+"/images/tw2gui/table/cell_shadow_y.png'); }</style>").appendTo("head");
			return true;
		},
		fetch: function() {
			if(this.interval !== false) clearInterval(this.interval);
			var event_times = {};
			var friends = []; /* to be removed */
			var friend = {}, total = 0, avail = 0, recv = 0;
			var server_time = Game.getServerTime(), activation_time, friend_time;
			if(this.timeLeft < 0) {
				$("#hiro_friends_container").slideUp(5000);
				throw "Event is over";
			}
			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;
						friend[val.player_id] = { name: val.name, activation_time: activation_time, recv: recv };
						friends.push({ id: val.player_id, name: val.name, activation_time: activation_time, recv: recv });
						++ total;
						if(activation_time + HiroFriends.eventInfo.cooldown - server_time <= 0) ++ avail;
					}
				});
				HiroFriends.friend = friend;
				HiroFriends.friends = friends;	/* to be removed */
				HiroFriends.avail = avail;
				HiroFriends.total = total;
				HiroFriends.interval = setInterval(function() { HiroFriends.fetch(); }, refreshMs);
				HiroFriends.updateCounter();
			});
		},
		getLog: function() {
			var hasNext = true;
			var limit = 100;
			var page = 1;
			var count = 0;
			var details;
			var maxDate = HiroFriends.log.lastLog;
			while(hasNext) {
				$.ajax({ type: "POST", url: "/game.php?window=ses&mode=log", data: { ses_id: HiroFriends.eventName, page: page, limit: limit }, async: false, success: function(data) {
					hasNext = data.hasNext;
					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 > maxDate) maxDate = val.date;
						switch(val.type) {
							case "friendDrop":
								details = JSON.parse(val.details);
								if(undefined !== HiroFriends.friend[details.player_id]) HiroFriends.friend[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 "itemUse":		HiroFriends.log.count_item += count; HiroFriends.log.received += count; break;
							case "wofPay":
								HiroFriends.log.used += count;
								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;
						}
					});
				} });
			}
			this.log.lastLog = maxDate;
			Chat.Request.Nop();
		},
		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();
			if(enableLog) this.getLog();
			return this.fetch().done(function() { 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.friend[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'));
				this.getPendingInvitations();
				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) {
						if(this.eventManager(eventName)) this.fetch();
						return false;
					}
				}
				return true;
			}
			++ tries;
			setTimeout(function() { HiroFriends.scriptInit(tries, maxTries); }, tries * 1e3);
		},
	}
	try { HiroFriends.scriptInit(0, 100); } catch(e) { }
});