Enhanced UI

Enhancements for the user interface of Ikariam.

当前为 2014-08-19 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Enhanced UI
  3. // @description Enhancements for the user interface of Ikariam.
  4. // @namespace Tobbe
  5. // @author Tobbe
  6. // @version 2.5
  7. //
  8. // @include http://s*.*.ikariam.*/*
  9. // @include http://s*.ikariam.gameforge.com/*
  10. //
  11. // @exclude http://support.*.ikariam.gameforge.com/*
  12. //
  13. // @resource languageGerman http://resources.ikascripts.de/74221/v2.3/languageGerman.json
  14. // @resource languageEnglish http://resources.ikascripts.de/74221/v2.3/languageEnglish.json
  15. // @resource languageLatvian http://resources.ikascripts.de/74221/v2.3/languageLatvian.json
  16. //
  17. // @grant unsafeWindow
  18. // @grant GM_setValue
  19. // @grant GM_getValue
  20. // @grant GM_deleteValue
  21. // @grant GM_listValues
  22. // @grant GM_getResourceText
  23. // @grant GM_xmlhttpRequest
  24. //
  25. // @history 2.5 Bugfix: Game language is recognized again.
  26. // @history 2.5 Bugfix: Works now in Greasemonkey 2.0+
  27. // @history 2.5 Checks now for updates on Greasy Fork.
  28. // @history 2.5 Removed code parts for mobile version.
  29. // @history 2.5 Removed alliance page improvement, as fixed in Ikariam v0.5.13
  30. //
  31. // @history 2.4 New Ikariam domain -> script can now be used with this domain
  32. //
  33. // @history 2.3 Feature: Formatting troop lists for posting in forums / personal messages. (desktop).
  34. // @history 2.3 Feature: Filling level of warehouse as bar in town view. (desktop).
  35. // @history 2.3 Feature: Link to mines / town hall when clicking on resources / citizens. (desktop).
  36. // @history 2.3 Feature: New versioning system.
  37. // @history 2.3 Bugfix: Some characters in links were not decoded correctly. (desktop)
  38. // @history 2.3 Bugfix: Watching a foreign city causes a abortion of the script. (desktop)
  39. // @history 2.3 Bugfix: The city dropdown sometimes was zoomed in world view. (desktop)
  40. // @history 2.3 Bugfix: Removed obsolete CSS styles for Firefox.
  41. // @history 2.3 Adjustments in the language files.
  42. // @history 2.3 Violent Monkey brings Greasemonkey functions to Opera.
  43. //
  44. // @history 2.2.1 Feature: Smaller icons in direct military tooltip. (desktop)
  45. // @history 2.2.1 Bugfix: Problems with update check and version numbers >= 10. (mobile & desktop)
  46. // @history 2.2.1 Bugfix: Problems with another script. (desktop)
  47. // @history 2.2.1 Bugfix: Problems with a wrong style in island view. (desktop)
  48. //
  49. // @history 2.2 Feature: Message signature can be set. (desktop)
  50. // @history 2.2 Feature: Button for faster sending of circular messages. (desktop)
  51. // @history 2.2 Feature: Make links in messages clickable. (desktop)
  52. // @history 2.2 Feature: Show town infos of colonizing cities. (desktop)
  53. // @history 2.2 Feature: Information about cargo / fleets is displayed directly in military view. (desktop)
  54. // @history 2.2 Feature: Script options: Sections on the script tab can be folded. (desktop)
  55. // @history 2.2 Bugfix: Tooltips with mouseover were not clickable anymore. (desktop)
  56. // @history 2.2 Changes in code for better overview.
  57. //
  58. // @history 2.1.1 Feature: Different styles for income in town view. (desktop)
  59. // @history 2.1.1 Feature: Remaining resources after upgrade. (desktop)
  60. // @history 2.1.1 Feature: Improved style for external alliance pages. (desktop)
  61. // @history 2.1.1 Feature: Refresh the missing / remaining resources in ruction view automatically. (desktop)
  62. //
  63. // @history 2.1 Feature: Show the missing resources in ruction view. (desktop)
  64. // @history 2.1 Feature: Show the hourly income directly in town view and add the daily production as popup. (desktop)
  65. // @history 2.1 Feature: Don't center town information in the town advisor. (desktop)
  66. // @history 2.1 Feature: Save the highscore data of alliance members and compare it with the actual value. (desktop)
  67. // @history 2.1 Bugfix: There was an error with a missing language package and seperators. (mobile & desktop)
  68. // @history 2.1 Bugfix: Some things in worldview were not resized correctly. (desktop)
  69. // @history 2.1 Prevent more than one script execution.
  70. // @history 2.1 Prevent more than one script option panel (the script option panel now is usable for other scripts, too).
  71. //
  72. // @history 2.0.1 Bugfix: Zooming was broken.
  73. // @history 2.0.1 Bugfix: Dropdown menus created by the script were broken.
  74. // @history 2.0.1 Bugfix: Tooltips in in Alliance / Military view were not shown correctly.
  75. //
  76. // @history 2.0 Feature: Cross-browser compatibility. (Firefox, Chrome, Opera)
  77. // @history 2.0 Feature: Latvian translation - thanks to Draxo. (mobile & desktop)
  78. // @history 2.0 Feature: Possibility to hide the update hint for a new script version. (mobile & desktop)
  79. // @history 2.0 Bugfix: Resizing the owner state in world view was broken. (desktop)
  80. // @history 2.0 Some changes in the code for easier copying of functions which should be used by more than one script.
  81. //
  82. // @history 1.7 Feature: Resizing banners when zooming is possible in city view, too. (desktop)
  83. // @history 1.7 Feature: The zoom buttons are available in world view, too. (desktop)
  84. // @history 1.7 Feature: Zooming with the mouse scroll is possible again (now with key, standard is ctrl). (desktop)
  85. // @history 1.7 Feature: Changes in the option panel due to the new zooming function features. (desktop)
  86. // @history 1.7 Bugfix: If resizing is enabled, zooming with the buttons will resize the banners, too. (desktop)
  87. // @history 1.7 Bugfix: The chat will not cause to much executions of script functions. (desktop)
  88. // @history 1.7 The language texts are integrated as resources, so that there is shorter code.
  89. // @history 1.7 Replace the GM_* functions by myGM.* to expand them easy and add new.
  90. //
  91. // @history 1.6 Feature: Possibility to hide only the bird swarm animation. (desktop)
  92. // @history 1.6 Feature: Easier upkeep reduction table. (mobile & desktop)
  93. // @history 1.6 Feature: Enhanced zoom function using the Ikariam zoom function. (desktop)
  94. // @history 1.6 Due to the use of Ikariam functions the code could be reduced.
  95. // @history 1.6 Code enhancements for shorter code.
  96. //
  97. // @history 1.5.1.1 Bugfix: Not all occurrences of hidden were changed.
  98. //
  99. // @history 1.5.1 Bugfix: Name of a class (hidden) is used by GF.
  100. //
  101. // @history 1.5 Feature: Options panel to enable/disable funtions and set settings. (mobile & desktop)
  102. // @history 1.5 Feature: Update interval can be set. (mobile & desktop)
  103. // @history 1.5 Feature: Manually check for updates. (mobile & desktop)
  104. // @history 1.5 Feature: Zoom funtion without resizing the whole view. (desktop)
  105. // @history 1.5 Feature: Move loading circle to another position. (desktop)
  106. // @history 1.5 Feature: Show tooltip in Alliance / Military view on mouseover. (desktop)
  107. // @history 1.5 Feature: Code better commented. More comments, so that it is easier to understand.
  108. // @history 1.5 Bugfix: Changed *.gif to *.png.
  109. // @history 1.5 Version numbers adjusted.
  110. //
  111. // @history 1.4.1 Feature: Support for mobile interface.
  112. // @history 1.4.1 Bugfix: Fixed bug with scrollbar in finances view. (desktop)
  113. //
  114. // @history 1.4 Feature: Ready for 0.5.0, but also supports 0.4.5 furthermore.
  115. // @history 1.4 Feature: Implemented support for different languages.
  116. // @history 1.4 Feature: Enhanced script updater.
  117. // @history 1.4 Feature: Cleaned up code.
  118. // @history 1.4 Feature: Rename the script to "Enhanced UI".
  119. // @history 1.4 Feature: Change the namespace to "Tobbe".
  120. // @history 1.4 Because of the change of namespace and name you have to delete the old script manually!
  121. //
  122. // @history 1.3.3 Bugfix: Problem with negative numbers and 0.4.2.4 fixed.
  123. //
  124. // @history 1.3.2 Feature: Own script updater.
  125. // @history 1.3.2 Bugfix: Remove everything what refered to other scripts.
  126. //
  127. // @history 1.3.1 Feature: New script updater.
  128. //
  129. // @history 1.3 Remove the script updater (Because of the problem with Greasemonkey scripts).
  130. //
  131. // @history 1.2.1 Feature: New style of update panel.
  132. // @history 1.2.1 Bugfix: Bug with ',' as seperator fixed.
  133. //
  134. // @history 1.2 Feature: Income in 24h added.
  135. // @history 1.2 Feature: Cleaned up code.
  136. //
  137. // @history 1.1 Feature: Update check implemented.
  138. //
  139. // @history 1.0 Initial release.
  140. // ==/UserScript==
  141.  
  142. /******************************************************************************************************************
  143. *******************************************************************************************************************
  144. *** ***
  145. *** The update function which is used in the script was developed by PhasmaExMachina and adapted by me (Tobbe). ***
  146. *** ***
  147. *******************************************************************************************************************
  148. ******************************************************************************************************************/
  149.  
  150. /**
  151. * Information about the Script.
  152. */
  153. var scriptInfo = {
  154. version: '2.5',
  155. id: 4369,
  156. name: 'Ikariam Enhanced UI',
  157. author: 'Tobbe',
  158. debug: false
  159. };
  160.  
  161. /**
  162. * Information about the language.
  163. */
  164. var languageInfo = {
  165. implemented: new Array('English', 'German', 'Latvian'),
  166. useResource: true,
  167. defaultText: {"default":{"update":{"notPossible":{"header":"No Update possible","text1":"It is not possible to check for updates for ","text2":". Please check manually for Updates for the script. The actual installed version is ","text3":". This message will appear again in four weeks."},"possible":{"header":"Update available","text1":"There is an update for ","text2":" available.","text3":"At the moment there is version ","text4":" installed. The newest version is ","history":"Version History","type":{"feature":"Feature(s)","bugfix":"Bugfix(es)","other":""},"button":{"install":"Install","hide":"Hide"}},"noNewExists":{"header":"No Update available","text1":"There is no new version for ","text2":" available. The newest version ","text3":" is installed."}},"notification":{"header":"Script notification","button":{"confirm":"OK","abort":"Abort"}}},"settings":{"kiloSep":",","decSep":".","left2right":true},"general":{"successful":"Your order has been carried out.","error":"There was an error in your request.","ctrl":"Ctrl","alt":"Alt","shift":"Shift","yes":"Yes","no":"No","show":"Show","hide":"Hide"},"balance":{"income":{"perHour":"Income per hour","perDay":"Income per day","start":"Income without reduction"},"upkeep":{"reason":{"0":"Troops","1":"Ships","2":"Troops & Ships"},"basic":"Basic Costs","supply":"Supply Costs","result":"Total Costs"}},"optionPanel":{"scripts":"Scripts","save":"Save settings!","section":{"module":{"title":"Modules","label":{"updateActive":"Search for updates automatically","incomeOnTopActive":"Show income on top in Balance view","upkeepReductionActive":"Show a short version of the upkeep reduction","missingResActive":"Show missing resources in construction view","resourceInfoActive":"Show the hourly income directly in town view","capacityInfoActive":"Show info bar for warehouse capacity","easyAccessActive":"Link resource number to town hall / mines","zoomActive":"Activate zoom in world view, island view, town view","messageSignatureActive":"Enable message signatures","easyCircularMsgActive":"Show button for faster sending of circular messages","replaceUrlsActive":"Make links in messages clickable","colonizingLinksActive":"Show information about colonizing cities","lcMoveActive":"Move loading circle to position bar","tooltipsAutoActive":"Show tooltips in alliance mebers view and military advisor automatically","directMilitaryTtActive":"Show information about cargo / fleets in military view without tooltips","unitInfoActive":"Show troop info","hideBirdsActive":"Hide the bird swarm.","nctAdvisorActive":"Don't center town information in the town advisor","niceAllyPageActive":"Show the external allypages better formatted","memberInfoActive":"Enable the possibility to save highscore data of alliance members"}},"update":{"title":"Update","label":{"interval":{"description":"Interval to search for updates:","option":{"hour":"1 hour","hour12":"12 hours","day":"1 day","day3":"3 days","week":"1 week","week2":"2 weeks","week4":"4 weeks"}},"manual":{"text1":"Search for updates for ","text2":"!"}}},"resInfoMissingRes":{"title":"Resource Information / Missing Resources","label":{"hourlyIncomeStyle":"Style of the hourly income in town view:","align":{"right":"Right align","left":"Left align","rightWithSeparation":"Right align with separation"},"capacityOrientationStyle":"Orientation of the bar","orientation":{"vertical":"Vertical","horizontal":"Horizontal","horizontalFull":"Horizontal, full length"},"hasBorder":"Has border","showBranchRes":"Show resources in trading post","showPositive":"Show also the remaining resources after an upgrade","showColoured":"Show the remaining resources coloured"}},"zoom":{"title":"Zoom function","label":{"zoomFactor":{"world":"Zoom worldmap:","island":"Zoom island view:","town":"Zoom town view:"},"scaleChildren":{"description":"Let banners and symbols in normal size when zooming when zooming in this view:","world":"Worldmap","island":"Island view","town":"Town view"},"accessKeys":"This keys must be pressed to zoom with the mouse:"}},"messageSignature":{"title":"Message signature","label":{"useServerSignature":"Use local signature","placementTop":"Insert signature above cited messages","signatureText":{"global":"Global signature, which could be used on every world:","server":"Local signature, which only could be used on this world:"}}}}},"memberInfo":{"show":"Alliance info","reset":"Reset","lastReset":"Time since the last reset:","noReset":"No reset so far."},"unitInfo":{"button":"Troop information","header":"Troops in ","units":{"label":"Units in ","own":"Own units","friends":"Allied units","enemies":"Enemy units"},"ships":{"label":"Ships in ","own":"Own ships","friends":"Allied ships","enemies":"Enemy ships"}},"resourceInfo":{"dailyIncome":{"wood":"Daily production building material:","wine":"Daily production wine:","marble":"Daily production marble:","glass":"Daily production crystal glass:","sulfur":"Daily production sulphur:"}},"easyCircularMsg":{"getLink":"Save link for circular message","send":"Send circular message"},"replacedUrl":{"notification":{"header":"Attention!","text1":"You're going to open the link ","text2":". This happens on your own risk. Proceed?"}},"zoomFunction":{"zoomIn":"Zoom in","zoomFactor":"Zoom factor","zoomOut":"Zoom out"},"name":{"resource":{"gold":"Gold","wood":"Building Material","wine":"Wine","marble":"Marble","glass":"Crystal Glass","sulfur":"Sulphur"},"unit":{"swordsman":"Swordsman","phalanx":"Hoplite","archer":"Archer","marksman":"Sulphur Carabineer","mortar":"Mortar","slinger":"Slinger","catapult":"Catapult","ram":"Battering Ram","steamgiant":"Steam Giant","bombardier":"Balloon-Bombardier","cook":"Cook","medic":"Doctor","girocopter":"Gyrocopter","spearman":"Spearman"},"ship":{"ballista":"Ballista Ship","catapult":"Catapult Ship","flamethrower":"Fire Ship","mortar":"Mortar Ship","ram":"Ram Ship","steamboat":"Steam Ram","rocketship":"Rocket Ship","submarine":"Diving Boat","paddlespeedship":"Paddle Speedboat","ballooncarrier":"Balloon Carrier","tender":"Tender","transport":"Merchant Ship"}}}
  168. };
  169.  
  170. /**********************************************************
  171. ***********************************************************
  172. ***** *****
  173. ***** Start of functions / variables for all Scripts. *****
  174. ***** *****
  175. ***********************************************************
  176. **********************************************************/
  177.  
  178. /**
  179. * Sets unsafeWindow / window to win for easier access.
  180. */
  181. var win = typeof unsafeWindow != 'undefined' ? unsafeWindow : window;
  182.  
  183. /**************************
  184. *** Debugging settings. ***
  185. **************************/
  186.  
  187. // For more information about commands that are available for the Firebug console see http://getfirebug.com/wiki/index.php/Console_API.
  188. if(scriptInfo.debug && win.debugConsole) {
  189. var conTmp = win.debugConsole;
  190. } else {
  191. var conTmp = {
  192. // Non static functions are set to 'null'.
  193. log: function () { return false; },
  194. info: function () { return false; },
  195. warn: function () { return false; },
  196. error: function () { return false; },
  197. debug: function () { return false; },
  198. assert: function () { return false; },
  199. clear: function () { return false; },
  200. dir: function () { return false; },
  201. dirxml: function () { return false; },
  202. trace: function () { return false; },
  203. group: function () { return false; },
  204. groupCollapsed: function () { return false; },
  205. groupEnd: function () { return false; },
  206. time: function () { return false; },
  207. timeEnd: function () { return false; },
  208. profile: function () { return false; },
  209. profileEnd: function () { return false; },
  210. count: function () { return false; },
  211. exception: function () { return false; },
  212. table: function () { return false; }
  213. };
  214. }
  215.  
  216. /**
  217. * Debugging console.
  218. */
  219. var con = conTmp;
  220.  
  221. /********************************
  222. *** Functions for all Strings ***
  223. ********************************/
  224.  
  225. /**
  226. * Replaces characters or whitespaces at the beginning of a string.
  227. *
  228. * @param String toRemove
  229. * A string containing the characters to remove (optional, if not set: trim whitespaces).
  230. *
  231. * @return String
  232. * The trimmed string.
  233. */
  234. String.prototype.ltrim = function(toRemove) {
  235. // Is there a string with the characters to remove?
  236. var special = !!toRemove;
  237.  
  238. // Return the trimmed string.
  239. return special ? this.replace(new RegExp('^[' + toRemove + ']+'), '') : this.replace(/^\s+/, '');
  240. };
  241.  
  242. /**
  243. * Replaces characters or whitespaces at the end of a string.
  244. *
  245. * @param String toRemove
  246. * A string containing the characters to remove (optional, if not set: trim whitespaces).
  247. *
  248. * @return String
  249. * The trimmed string.
  250. */
  251. String.prototype.rtrim = function(toRemove) {
  252. // Is there a string with the characters to remove?
  253. var special = !!toRemove;
  254.  
  255. // Return the trimmed string.
  256. return special ? this.replace(new RegExp('[' + toRemove + ']+$'), '') : this.replace(/\s+$/, '');
  257. };
  258.  
  259. /**
  260. * Replaces characters or whitespaces at the beginning and end of a string.
  261. *
  262. * @param String toRemove
  263. * A string containing the characters to remove (optional, if not set: trim whitespaces).
  264. *
  265. * @return String
  266. * The trimmed string.
  267. */
  268. String.prototype.trim = function(toRemove) {
  269. // Return the trimmed string.
  270. return this.ltrim(toRemove).rtrim(toRemove);
  271. };
  272.  
  273. /**
  274. * Encodes HTML-special characters in a string.
  275. *
  276. * @return String
  277. * The encoded string.
  278. */
  279. String.prototype.encodeHTML = function() {
  280. // Set the characters to encode.
  281. var characters = {
  282. '&': '&',
  283. '"': '"',
  284. '\'': ''',
  285. '<': '&lt;',
  286. '>': '&gt;'
  287. };
  288. // Return the encoded string.
  289. return this.replace(/([\&"'<>])/g, function(string, symbol) { return characters[symbol]; });
  290. };
  291.  
  292. /**
  293. * Decodes HTML-special characters in a string.
  294. *
  295. * @return String
  296. * The decoded string.
  297. */
  298. String.prototype.decodeHTML = function() {
  299. // Set the characters to decode.
  300. var characters = {
  301. '&amp;': '&',
  302. '&quot;': '"',
  303. '&apos;': '\'',
  304. '&lt;': '<',
  305. '&gt;': '>'
  306. };
  307. // Return the decoded string.
  308. return this.replace(/(&quot;|&apos;|&lt;|&gt;|&amp;)/g, function(string, symbol) { return characters[symbol]; });
  309. };
  310.  
  311. /**
  312. * Repeats a string a specified number of times.
  313. *
  314. * @param int nr
  315. * The number of times to repeat the string.
  316. *
  317. * @return String
  318. * The repeated string.
  319. */
  320. String.prototype.repeat = function(nr) {
  321. // Storage for the result.
  322. var ret = this;
  323. // Repeat the string.
  324. for(var i = 1; i < nr; i++) {
  325. ret += this;
  326. }
  327. // Return the string.
  328. return ret;
  329. };
  330.  
  331. /********************************
  332. *** myGM / Updater / Language ***
  333. ********************************/
  334.  
  335. /**
  336. * myGM for cross-browser compatibility of the GM_* functions. (use myGM.* instead of GM_*)
  337. * Also there are general used functions stored.
  338. */
  339. myGM = {
  340. /**
  341. * If it is possible to use the default GM_*Value functions, true, otherwise false.
  342. */
  343. canUseGmStorage: false,
  344.  
  345. /**
  346. * If it is possible to use the default GM_getResourceText function, true, otherwise false.
  347. */
  348. canUseGmRessource: false,
  349.  
  350. /**
  351. * If it is possible to use the default GM_xmlhttpRequest function, true, otherwise false.
  352. */
  353. canUseGmXhr: false,
  354.  
  355. /**
  356. * If it is possible to use the localStorage, true, otherwise false.
  357. */
  358. canUseLocalStorage: false,
  359.  
  360. /**
  361. * The prefix which schuld be added to the values stored in localStorage / cookies.
  362. */
  363. prefix: '',
  364.  
  365. /**
  366. * Storage for the style sheets which will be added by the script.
  367. */
  368. styleSheets: new Array(),
  369.  
  370. /**
  371. * Storage for the notification id.
  372. */
  373. notificationId: 0,
  374.  
  375. /**
  376. * Init myGM.
  377. */
  378. init: function() {
  379. // Set the prefix for the script.
  380. this.prefix = 'script' + scriptInfo.id;
  381.  
  382. // Set the possibility to use the different storages.
  383. this.canUseLocalStorage = !!win.localStorage;
  384. this.canUseGmStorage = !(typeof GM_getValue == 'undefined' || (typeof GM_getValue.toString == 'function' && GM_getValue.toString().indexOf('not supported') > -1))
  385. && !(typeof GM_setValue == 'undefined' || (typeof GM_setValue.toString == 'function' && GM_setValue.toString().indexOf('not supported') > -1))
  386. && !(typeof GM_deleteValue == 'undefined' || (typeof GM_deleteValue.toString == 'function' && GM_deleteValue.toString().indexOf('not supported') > -1))
  387. && !(typeof GM_listValues == 'undefined' || (typeof GM_listValues.toString == 'function' && GM_listValues.toString().indexOf('not supported') > -1));
  388.  
  389. // Set the possibility to use the GM_resource.
  390. this.canUseGmRessource = !(typeof GM_getResourceText == 'undefined' || (typeof GM_getResourceText.toString == 'function' && GM_getResourceText.toString().indexOf('not supported') > -1));
  391.  
  392. // Set the possibillity to use the GM_xhr.
  393. this.canUseGmXhr = !(typeof GM_xmlhttpRequest == 'undefined' || (typeof GM_xmlhttpRequest.toString == 'function' && GM_xmlhttpRequest.toString().indexOf('not supported') > -1));
  394.  
  395. // Set the close image. It is stored as a data URI. If you copy it to your browser, a red cross (16px * 16px) will be shown.
  396. var closeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAIhSURBVDjLlZPrThNRFIWJicmJz6BWiYbIkYDEG0JbBiitDQgm0PuFXqSAtKXtpE2hNuoPTXwSnwtExd6w0pl2OtPlrphKLSXhx07OZM769qy19wwAGLhM1ddC184+d18QMzoq3lfsD3LZ7Y3XbE5DL6Atzuyilc5Ciyd7IHVfgNcDYTQ2tvDr5crn6uLSvX+Av2Lk36FFpSVENDe3OxDZu8apO5rROJDLo30+Nlvj5RnTlVNAKs1aCVFr7b4BPn6Cls21AWgEQlz2+Dl1h7IdA+i97A/geP65WhbmrnZZ0GIJpr6OqZqYAd5/gJpKox4Mg7pD2YoC2b0/54rJQuJZdm6Izcgma4TW1WZ0h+y8BfbyJMwBmSxkjw+VObNanp5h/adwGhaTXF4NWbLj9gEONyCmUZmd10pGgf1/vwcgOT3tUQE0DdicwIod2EmSbwsKE1P8QoDkcHPJ5YESjgBJkYQpIEZ2KEB51Y6y3ojvY+P8XEDN7uKS0w0ltA7QGCWHCxSWWpwyaCeLy0BkA7UXyyg8fIzDoWHeBaDN4tQdSvAVdU1Aok+nsNTipIEVnkywo/FHatVkBoIhnFisOBoZxcGtQd4B0GYJNZsDSiAEadUBCkstPtN3Avs2Msa+Dt9XfxoFSNYF/Bh9gP0bOqHLAm2WUF1YQskwrVFYPWkf3h1iXwbvqGfFPSGW9Eah8HSS9fuZDnS32f71m8KFY7xs/QZyu6TH2+2+FAAAAABJRU5ErkJggg%3D%3D';
  397.  
  398. // Set the notification style.
  399. this.addStyle(
  400. "." + this.prefix + "notificationBackground { z-index: 1000000000000; position: fixed; visibility: visible; top: 0px; left: 0px; width: 100%; height: 100%; padding: 0; background-color: #000; opacity: .7; } \
  401. ." + this.prefix + "notificationPanelContainer { z-index: 1000000000001; position: fixed; visibility: visible; top: 100px; left: 50%; width: 500px; height: 370px; margin-left: -250px; padding: 0; text-align: left; color: #333; font: 12px Arial,Helvetica,sans-serif; } \
  402. ." + this.prefix + "notificationPanel { position: relative; top: 0px; left: 0px; background-color: transparent; border: 0 none; overflow: hidden; } \
  403. ." + this.prefix + "notificationPanelHeader { height: 39px; background: none repeat scroll 0 0 transparent; font-weight: bold; line-height: 2; white-space: nowrap; } \
  404. ." + this.prefix + "notificationPanelHeaderL { } \
  405. ." + this.prefix + "notificationPanelHeaderR { } \
  406. ." + this.prefix + "notificationPanelHeaderM { height: 24px; background-color: #999; padding: 5px; border: 3px solid #000; border-radius: 5px; color: #811709; line-height: 1.34em; font-size: 15pt; } \
  407. ." + this.prefix + "notificationPanelBody { max-height: 311px; height: 100%; background: none repeat scroll 0 0 transparent; } \
  408. ." + this.prefix + "notificationPanelBodyL { } \
  409. ." + this.prefix + "notificationPanelBodyR { } \
  410. ." + this.prefix + "notificationPanelBodyM { height: 100%; background-color: #F5F5F5; border-left: 3px solid #000; border-right: 3px solid #000; border-top-left-radius: 5px; border-top-right-radius: 5px; padding: 0 10px; font-size: 14px; } \
  411. ." + this.prefix + "notificationPanelBodyMTop { max-height: 100px; line-height: 2; } \
  412. ." + this.prefix + "notificationPanelBodyMTop b { line-height: 3.5; font-size: 110%; } \
  413. ." + this.prefix + "notificationPanelBodyM a { color: #811709; font-weight: bold; } \
  414. ." + this.prefix + "notificationPanelBodyM h2 { font-weight: bold; } \
  415. ." + this.prefix + "notificationPanelBodyMContent { max-height: 265px; padding: 10px; background-color: #FFF; border: 1px dotted #4D4D4D; font: 14px Arial,Helvetica,sans-serif; color: #333; border-collapse: separate; overflow-y:auto; margin-top: 5px; } \
  416. ." + this.prefix + "notificationPanelBodyMBottom { max-height: 170px; padding: 10px; background-color: #FFF; border: 1px dotted #4D4D4D; font: 14px Arial,Helvetica,sans-serif; color: #333; border-collapse: separate; overflow-y:auto; } \
  417. ." + this.prefix + "notificationPanelBodyPlaceholder { height: 20px; } \
  418. ." + this.prefix + "notificationPanelFooter { height: 20px; background: none repeat scroll 0 0 transparent; } \
  419. ." + this.prefix + "notificationPanelFooterL { } \
  420. ." + this.prefix + "notificationPanelFooterR { } \
  421. ." + this.prefix + "notificationPanelFooterM { background-color: #F5F5F5; border-bottom: 3px solid #000; border-left: 3px solid #000; border-right: 3px solid #000; border-bottom-left-radius: 5px; border-bottom-right-radius: 5px; padding: 3px 0 2px 3px; font-size: 77%; } \
  422. ." + this.prefix + "notificationPanelClose { cursor: pointer; position: absolute; top: 10px; right: 10px; width: 16px; height: 16px; background-image: url('" + closeImage + "'); } \
  423. ." + this.prefix + "notificationPanelButtonWrapper { bottom: -4px; position: absolute; margin: 10px auto; width: 100%; text-align: center; } \
  424. ." + this.prefix + "notificationPanelButton { background-color: #DADEDE; border-color: #C0C0C0 #4D4D4D #4D4D4D #C0C0C0; border-style: double; border-width: 3px; border-radius: 5px; cursor: pointer; display: inline; font-weight: bold; margin: 0px 5px; padding: 2px 10px; text-align: center; font-size: 12px; width: 100px; } \
  425. ." + this.prefix + "notificationPanelButton:hover { color: #444; } \
  426. ." + this.prefix + "notificationPanelButton:active { border-color: #4D4D4D #C0C0C0 #C0C0C0 #4D4D4D; border-style: double; border-width: 3px; padding: 3px 10px 1px; } \
  427. ." + this.prefix + "notificationPanelButtonConfirm { } \
  428. ." + this.prefix + "notificationPanelButtonAbort { }",
  429. 'notification'
  430. );
  431.  
  432. // Set the updater style.
  433. this.addStyle(
  434. "." + this.prefix + "updateTable { border-collapse: separate; border-spacing: 2px; } \
  435. ." + this.prefix + "updateDataType { width: 100px; padding: 5px 0px 5px 5px; border: 1px solid #999; } \
  436. ." + this.prefix + "updateDataInfo { width: 300px; padding: 5px 5px 5px 20px; border: 1px solid #999; } \
  437. ." + this.prefix + "updateDataInfo ul li { list-style: disc outside none; }",
  438. 'updater'
  439. );
  440. },
  441.  
  442. /**
  443. * Store a value specified by a key.
  444. *
  445. * @param String key
  446. * The key of the value.
  447. * @param mixed value
  448. * The value to store.
  449. */
  450. setValue: function(key, value) {
  451. var toStore = '';
  452. // Stringify the value to store also arrays.
  453. toStore = JSON.stringify(value);
  454.  
  455. // If the use of the default GM_setValue ist possible, use it.
  456. if(this.canUseGmStorage) {
  457. // Store the value.
  458. GM_setValue(key, toStore);
  459.  
  460. // Otherwise use the local storage if possible.
  461. } else if(this.canUseLocalStorage) {
  462. // Store the value.
  463. win.localStorage.setItem(this.prefix + key, toStore);
  464.  
  465. // Otherwise use cookies.
  466. } else {
  467. // Prepare the cookie name and value.
  468. var data = escape(this.prefix + key) + '=' + escape(toStore);
  469.  
  470. // Set the expire date to January 1st, 2020.
  471. var expire = 'expires=' + (new Date(2020, 0, 1, 0, 0, 0, 0)).toGMTString();
  472.  
  473. // Set the path to root.
  474. var path = 'path=/';
  475.  
  476. // Made the cookie accessible from all servers.
  477. var domain = 'domain=ikariam.com';
  478.  
  479. // Set the cookie.
  480. win.document.cookie = data + ';' + expire + ';' + path + ';' + domain;
  481. }
  482. },
  483.  
  484. /**
  485. * Get a value and return it.
  486. *
  487. * @param String key
  488. * The key of the value.
  489. * @param mixed defaultValue
  490. * The value which is set if the value is not set.
  491. *
  492. * @return mixed
  493. * The stored value.
  494. */
  495. getValue: function(key, defaultValueOld) {
  496. defaultValue = JSON.stringify(defaultValueOld);
  497. // Storage for the value.
  498. var value = defaultValue;
  499.  
  500. // If the use of the default GM_getValue ist possible, use it.
  501. if(this.canUseGmStorage) {
  502. // Get the value.
  503. value = GM_getValue(key, defaultValue);
  504.  
  505. // Otherwise use the local storage if possible.
  506. } else if(this.canUseLocalStorage) {
  507. // Get the value.
  508. var valueTmp = win.localStorage.getItem(this.prefix + key);
  509.  
  510. // If the value is not set, let the default value set.
  511. if(valueTmp) {
  512. value = valueTmp;
  513. }
  514.  
  515. // Otherwise use cookies.
  516. } else {
  517. // Get all cookies.
  518. var allCookies = document.cookie.split("; ");
  519.  
  520. // Loop over all cookies.
  521. for(var i = 0; i < allCookies.length; i++) {
  522. // Get the key and value of a cookie.
  523. var oneCookie = allCookies[i].split("=");
  524.  
  525. // If the key is the correct key, get the value.
  526. if(oneCookie[0] == escape(this.prefix + key)) {
  527. // Get the value and abort the loop.
  528. value = unescape(oneCookie[1]);
  529. break;
  530. }
  531. }
  532. }
  533. // Return the value (parsed for the correct return type).
  534. return JSON.parse(value);
  535. },
  536.  
  537. /**
  538. * Delete a value specified by a key.
  539. *
  540. * @param String key
  541. * The key of the value.
  542. */
  543. deleteValue: function(key) {
  544. // If the use of the default GM_deleteValue ist possible, use it.
  545. if(this.canUseGmStorage) {
  546. // Delete the value.
  547. GM_deleteValue(key);
  548.  
  549. // Otherwise use the local storage if possible.
  550. } else if(this.canUseLocalStorage) {
  551. // Remove the value.
  552. win.localStorage.removeItem(this.prefix + key);
  553.  
  554. // Otherwise use cookies.
  555. } else {
  556. // Prepare the cookie name.
  557. var data = escape(this.prefix + key) + '=';
  558.  
  559. // Set the expire date to January 1st, 2000 (this will delete the cookie).
  560. var expire = 'expires=' + (new Date(2000, 0, 1, 0, 0, 0, 0)).toGMTString();
  561.  
  562. // Set the path to root.
  563. var path = 'path=/';
  564.  
  565. // Made the cookie accessible from all servers.
  566. var domain = 'domain=ikariam.com';
  567.  
  568. // Set the cookie.
  569. win.document.cookie = data + ';' + expire + ';' + path + ';' + domain;
  570. }
  571. },
  572.  
  573. /**
  574. * Returns an array with the keys of all values stored by the script.
  575. *
  576. * @return mixed[]
  577. * The array with all keys.
  578. */
  579. listValues: function() {
  580. // Create an array for the storage of the values keys.
  581. var key = new Array();
  582.  
  583. // If the use of the default GM_listValues ist possible, use it.
  584. if(this.canUseGmStorage) {
  585. // Store the key(s) to the array.
  586. key = GM_listValues();
  587.  
  588. // Otherwise use the local storage if possible.
  589. } else if(this.canUseLocalStorage) {
  590. // Loop over all stored values.
  591. for(var i = 0; i < win.localStorage.length; i++) {
  592. // Get the key name of the key with the number i.
  593. var keyName = win.localStorage.key(i);
  594.  
  595. // If the value is set by the script, push the key name to the array.
  596. if(keyName.indexOf(this.prefix) != -1) {
  597. key.push(keyName.replace(this.prefix, ''));
  598. }
  599. }
  600.  
  601. // Otherwise use cookies.
  602. } else {
  603. // Get all cookies.
  604. var allCookies = document.cookie.split("; ");
  605.  
  606. // Loop over all cookies.
  607. for(var i = 0; i < allCookies.length; i++) {
  608. // Get the key name of a cookie.
  609. var keyName = unescape(allCookies[i].split("=")[0]);
  610.  
  611. // If the key value is set by the script, push the key name to the array.
  612. if(keyName.indexOf(this.prefix) != -1) {
  613. key.push(keyName.replace(this.prefix, ''));
  614. }
  615. }
  616. }
  617.  
  618. // Return all keys.
  619. return key;
  620. },
  621.  
  622. /**
  623. * Adds a style element to the head of the page and return it.
  624. *
  625. * @param String styleRules
  626. * The style rules to be set.
  627. * @param String id
  628. * An id for the style set, to have the possibility to delete it. (optional, if none is set, the stylesheet is not stored)
  629. * @param boolean overwrite
  630. * If a style with id should overwrite an existing style.
  631. *
  632. * @return boolean
  633. * If the stylesheet was stored with the id.
  634. */
  635. addStyle: function(styleRules, id, overwrite) {
  636. // If the element was stored is saved here.
  637. var storedWithId = false;
  638.  
  639. // If overwrite, remove the old style sheet.
  640. if(overwrite && overwrite == true) {
  641. this.removeStyle(id);
  642. }
  643.  
  644. // If the stylesheet doesn't exists.
  645. if(!id || (id && !this.styleSheets[id])) {
  646. // Create a new style element and set the style rules.
  647. var style = this.addElement('style', document.head);
  648. style.type = 'text/css';
  649. style.innerHTML = styleRules;
  650.  
  651. // If an id is set, store it.
  652. if(id) {
  653. this.styleSheets[id] = style;
  654. storedWithId = true;
  655. }
  656. }
  657.  
  658. // Return if the stylesheet was stored with an id.
  659. return storedWithId;
  660. },
  661.  
  662. /**
  663. * Removes a style element set by the script.
  664. *
  665. * @param String id
  666. * The id of the stylesheet to delete.
  667. *
  668. * @return boolean
  669. * If the stylesheet could be deleted.
  670. */
  671. removeStyle: function(id) {
  672. // Stores if the stylesheet could be removed.
  673. var removed = false;
  674.  
  675. // If there is an id set and a stylesheet with the id exists.
  676. if(id && this.styleSheets[id]) {
  677. // Remove the stylesheet from the page.
  678. document.head.removeChild(this.styleSheets[id]);
  679.  
  680. // Remove the stylesheet from the array.
  681. delete this.styleSheets[id];
  682.  
  683. // Set removed to true.
  684. removed = true;
  685. }
  686.  
  687. // Return if the stylesheet could be removed.
  688. return removed;
  689. },
  690.  
  691. /**
  692. * Returns the content of a resource parsed with JSON.parse.
  693. *
  694. * @param String name
  695. * The name of the resource to parse.
  696. */
  697. getResourceParsed: function(name) {
  698. // Storage for the response text.
  699. var responseText = '';
  700.  
  701. // Function for safer parsing.
  702. var safeParse = function(key, value) {
  703. // If the value is a function, return just the string, so it is not executable.
  704. if(typeof value === 'function' || Object.prototype.toString.apply(value) === '[object function]') {
  705. return value.toString();
  706. }
  707.  
  708. // Return the value.
  709. return value;
  710. };
  711.  
  712. // If the use of the default GM_getRessourceText ist possible, use it.
  713. if(this.canUseGmRessource) {
  714. // Set the parsed text.
  715. responseText = GM_getResourceText(name);
  716.  
  717. // Otherwise perform a xmlHttpRequest.
  718. } else {
  719. // Perform the xmlHttpRequest.
  720. responseText = this.xhr({
  721. method: 'GET',
  722. url: 'http://resources.ikascripts.de/' + scriptInfo.id + '/v' + scriptInfo.version + '/' + name + '.json',
  723. headers: { 'User-agent': navigator.userAgent, 'Accept': 'text/html' },
  724. synchronous: true,
  725. onload: function(response) { return false; }
  726. });
  727. }
  728. return JSON.parse(responseText.trim(), safeParse);
  729. },
  730.  
  731. /**
  732. * Makes a cross-site XMLHttpRequest.
  733. *
  734. * @param mixed[] args
  735. * The arguments the request needs. (specified here: http://wiki.greasespot.net/GM_xmlhttpRequest)
  736. */
  737. xhr: function(args) {
  738. // Storage for the result of the request.
  739. var responseText;
  740.  
  741. // Check if all required data is given.
  742. if(!args.method || !args.url || !args.onload) {
  743. return false;
  744. }
  745.  
  746. // If the use of the default GM_xmlhttpRequest ist possible, use it.
  747. if(this.canUseGmXhr) {
  748. // Sent the request.
  749. var response = GM_xmlhttpRequest(args);
  750.  
  751. // Get the response text.
  752. responseText = response.responseText;
  753.  
  754. // Otherwise show a hint for the missing possibility to fetch the data.
  755. } else {
  756. // Storage if the link fetches metadata from userscripts.org
  757. var usoUpdate = (args.url.search(/^http:\/\/userscripts.org\/scripts\/source\/[0-9]+\.meta\.js$/i) != -1);
  758. var isJSON = (args.url.search(/\.json$/i) != -1);
  759.  
  760. // If it is a metadata request.
  761. if(usoUpdate) {
  762. // Set the update interval to max.
  763. this.setValue('updater_updateInterval', 28 * 24 * 60 * 60);
  764.  
  765. // Set the notification text.
  766. var notificationText = {
  767. header: Language.$('default_update_notPossible_header'),
  768. body: Language.$('default_update_notPossible_text1') + '<a href="http://userscripts.org/scripts/show/' + scriptInfo.id + '" target="_blank" >' + scriptInfo.name + '</a>' + Language.$('default_update_notPossible_text2') + scriptInfo.version + Language.$('default_update_notPossible_text3')
  769. };
  770.  
  771. // Show a notification.
  772. this.notification(notificationText);
  773.  
  774. responseText = true;
  775.  
  776. // Otherwise if it is JSON.
  777. } else if(isJSON) {
  778. // Do the request with a string indicating the error.
  779. args.onload('{ "is_error": true }');
  780.  
  781. // Return a string indicating the error.
  782. responseText = '{ "is_error": true }';
  783.  
  784. // Otherwise.
  785. } else {
  786. responseText = false;
  787. }
  788. }
  789.  
  790. // Return the responseText.
  791. return responseText;
  792. },
  793.  
  794. /**
  795. * Shows a notification to the user.
  796. *
  797. * Possible notification texts:
  798. * text.header (optional)
  799. * text.body or text.bodyTop & text.bodyBottom
  800. * text.confirm (optional)
  801. * text.abort (optional)
  802. *
  803. * @param String[] text
  804. * The notification texts.
  805. * @param function[] callback
  806. * The callbacks for confirm and abort. (optional, default: close panel)
  807. *
  808. * @return int
  809. * The notification id.
  810. */
  811. notification: function(text, callback) {
  812. // Get the notification id.
  813. this.notificationId += 1;
  814. var notificationId = this.notificationId;
  815.  
  816. // Create the background and the container.
  817. myGM.addElement('div', document.body, 'notificationBackground' + notificationId, 'notificationBackground', null, true);
  818. var notificationPanelContainer = myGM.addElement('div', document.body, 'notificationPanelContainer' + notificationId, 'notificationPanelContainer', null, true);
  819. var notificationPanel = myGM.addElement('div', notificationPanelContainer, 'notificationPanel' + notificationId, 'notificationPanel', null, true);
  820.  
  821. // Create the notification panel header.
  822. var notificationPanelHeader = myGM.addElement('div', notificationPanel, 'notificationPanelHeader' + notificationId, 'notificationPanelHeader', null, true);
  823. var notificationPanelHeaderL = myGM.addElement('div', notificationPanelHeader, 'notificationPanelHeaderL' + notificationId, 'notificationPanelHeaderL', null, true);
  824. var notificationPanelHeaderR = myGM.addElement('div', notificationPanelHeaderL, 'notificationPanelHeaderR' + notificationId, 'notificationPanelHeaderR', null, true);
  825. var notificationPanelHeaderM = myGM.addElement('div', notificationPanelHeaderR, 'notificationPanelHeaderM' + notificationId, 'notificationPanelHeaderM', null, true);
  826.  
  827. // Create the notification panel body.
  828. var notificationPanelBody = myGM.addElement('div', notificationPanel, 'notificationPanelBody' + notificationId, 'notificationPanelBody', null, true);
  829. var notificationPanelBodyL = myGM.addElement('div', notificationPanelBody, 'notificationPanelBodyL' + notificationId, 'notificationPanelBodyL', null, true);
  830. var notificationPanelBodyR = myGM.addElement('div', notificationPanelBodyL, 'notificationPanelBodyR' + notificationId, 'notificationPanelBodyR', null, true);
  831. var notificationPanelBodyM = myGM.addElement('div', notificationPanelBodyR, 'notificationPanelBodyM' + notificationId, 'notificationPanelBodyM', null, true);
  832. if(text.body) {
  833. var notificationPanelBodyMContent = myGM.addElement('div', notificationPanelBodyM, 'notificationPanelBodyMContent' + notificationId, 'notificationPanelBodyMContent', null, true);
  834. } else {
  835. var notificationPanelBodyMTop = myGM.addElement('div', notificationPanelBodyM, 'notificationPanelBodyMTop' + notificationId, 'notificationPanelBodyMTop', null, true);
  836. var notificationPanelBodyMBottom = myGM.addElement('div', notificationPanelBodyM, 'notificationPanelBodyMBottom' + notificationId, 'notificationPanelBodyMBottom', null, true);
  837. }
  838. myGM.addElement('div', notificationPanelBodyM, 'notificationPanelBodyPlaceholder' + notificationId, 'notificationPanelBodyPlaceholder', null, true);
  839.  
  840. // Create the notification panel footer.
  841. var notificationPanelFooter = myGM.addElement('div', notificationPanel, 'notificationPanelFooter' + notificationId, 'notificationPanelFooter', null, true);
  842. var notificationPanelFooterL = myGM.addElement('div', notificationPanelFooter, 'notificationPanelFooterL' + notificationId, 'notificationPanelFooterL', null, true);
  843. var notificationPanelFooterR = myGM.addElement('div', notificationPanelFooterL, 'notificationPanelFooterR' + notificationId, 'notificationPanelFooterR', null, true);
  844. var notificationPanelFooterM = myGM.addElement('div', notificationPanelFooterR, 'notificationPanelFooterM' + notificationId, 'notificationPanelFooterM', null, true);
  845.  
  846. // Create the button wrapper.
  847. var notificationPanelButtonWrapper = myGM.addElement('div', notificationPanel, 'notificationPanelButtonWrapper' + notificationId, 'notificationPanelButtonWrapper', null, true);
  848. // Create the confirm button.
  849. var notificationPanelConfirm = myGM.addElement('input', notificationPanelButtonWrapper, 'notificationPanelConfirm' + notificationId, new Array('notificationPanelButton', 'notificationPanelButtonConfirm'), null, true);
  850. notificationPanelConfirm.type = 'button';
  851. notificationPanelConfirm.value = text.confirm ? text.confirm : Language.$('default_notification_button_confirm');
  852.  
  853. // Create the abort button if needed.
  854. if(callback && callback.abort) {
  855. var notificationPanelAbort = myGM.addElement('input', notificationPanelButtonWrapper, 'notificationPanelAbort' + notificationId, new Array('notificationPanelButton', 'notificationPanelButtonAbort'), null, true);
  856. notificationPanelAbort.type = 'button';
  857. notificationPanelAbort.value = text.abort ? text.abort : Language.$('default_notification_button_abort');
  858. }
  859. // Insert the texts into header, body and footer.
  860. notificationPanelHeaderM.innerHTML = (text.header ? text.header : Language.$('default_notification_header')) + '<div id="' + this.prefix + 'notificationPanelClose' + notificationId + '" class="' + this.prefix + 'notificationPanelClose"></div>';
  861. notificationPanelFooterM.innerHTML = scriptInfo.name + ' v' + scriptInfo.version;
  862. if(text.body) {
  863. notificationPanelBodyMContent.innerHTML = text.body;
  864. } else {
  865. notificationPanelBodyMTop.innerHTML = text.bodyTop ? text.bodyTop : '';
  866. notificationPanelBodyMBottom.innerHTML = text.bodyBottom ? text.bodyBottom : '';
  867. }
  868.  
  869. // Function to close the notification panel.
  870. var closeNotificationPanel = function() {
  871. // Remove the notification background.
  872. document.body.removeChild(myGM.$('#' + myGM.prefix + 'notificationBackground' + notificationId));
  873.  
  874. // Remove the notification panel.
  875. document.body.removeChild(myGM.$('#' + myGM.prefix + 'notificationPanelContainer' + notificationId));
  876. };
  877.  
  878. // Add event listener to the buttons to close / install.
  879. this.$('#' + this.prefix + 'notificationPanelClose' + notificationId).addEventListener('click', closeNotificationPanel, false);
  880.  
  881. if(callback && callback.confirm) {
  882. notificationPanelConfirm.addEventListener('click', function() { closeNotificationPanel(); callback.confirm(); }, false);
  883. } else {
  884. notificationPanelConfirm.addEventListener('click', closeNotificationPanel, false);
  885. }
  886.  
  887. if(callback && callback.abort) {
  888. notificationPanelAbort.addEventListener('click', function() { closeNotificationPanel(); callback.abort(); }, false);
  889. }
  890. return notificationId;
  891. },
  892.  
  893. /**
  894. * Gets the first matching child element by a query and returns it.
  895. *
  896. * @param String query
  897. * The query for the element.
  898. * @param element parent
  899. * The parent element. (optional, default document)
  900. *
  901. * @return element
  902. * The element.
  903. */
  904. $: function(query, parent) {
  905. return this.$$(query, parent)[0];
  906. },
  907.  
  908. /**
  909. * Gets all matching child elements by a query and returns them.
  910. *
  911. * @param String query
  912. * The query for the elements.
  913. * @param element parent
  914. * The parent element. (optional, default document)
  915. *
  916. * @return element[]
  917. * The elements.
  918. */
  919. $$: function(query, parent) {
  920. // If there is no parent set, set it to document.
  921. if(!parent) parent = document;
  922.  
  923. // Return the elements.
  924. return Array.prototype.slice.call(parent.querySelectorAll(query));
  925. },
  926.  
  927. /**
  928. * Creates a new element and adds it to a parent.
  929. *
  930. * @param String type
  931. * The type of the new element.
  932. * @param element parent
  933. * The parent of the new element.
  934. * @param int id
  935. * The last part of the id of the element. (optional, if not set, no id will be set)
  936. * @param String || String[] classes
  937. * The class(es) of the element. (optional, if not set, no class will be set)
  938. * @param mixed[] style
  939. * The styles of the element. (optional, if not set, no style will be set)
  940. * @param boolean || boolean[] hasPrefix
  941. * If no prefix should be used. (optional, if not set, a prefix will be used for id and no prefix will be used for classes)
  942. * @param element nextSib
  943. * The next sibling of the element. (optional, if not set, the element will be added at the end)
  944. *
  945. * @return element
  946. * The new element.
  947. */
  948. addElement: function(type, parent, id, classes, style, hasPrefix, nextSib) {
  949. // Create the new Element.
  950. var newElement = document.createElement(type);
  951.  
  952. // If there is a id, set it.
  953. if(id) {
  954. // Get the id prefix.
  955. var idPrefix = !(hasPrefix == false || (hasPrefix && hasPrefix.id == false)) ? this.prefix : '';
  956.  
  957. // Set the id.
  958. newElement.id = idPrefix + id;
  959. }
  960.  
  961. // Add all classes.
  962. if(classes && classes != '') {
  963. // Get the class prefix.
  964. var classPrefix = !!(hasPrefix == true || (hasPrefix && hasPrefix.classes == true)) ? this.prefix : '';
  965.  
  966. // Set the class(es).
  967. if(typeof classes == 'string') {
  968. newElement.classList.add(classPrefix + classes);
  969. } else {
  970. for(var i = 0; i < classes.length; i++) {
  971. newElement.classList.add(classPrefix + classes[i]);
  972. }
  973. }
  974. }
  975.  
  976. if(style) {
  977. for(var i = 0; i < style.length; i++) {
  978. newElement.style[style[i][0]] = style[i][1];
  979. }
  980. }
  981.  
  982. // If there is the next sibling defined, insert it before it.
  983. if(nextSib) {
  984. parent.insertBefore(newElement, nextSib);
  985.  
  986. // Otherwise insert it at the end.
  987. } else {
  988. parent.appendChild(newElement);
  989. }
  990.  
  991. // Return the new element.
  992. return newElement;
  993. }
  994. };
  995.  
  996. /**
  997. * Functions for updater.
  998. */
  999. Updater = {
  1000. /**
  1001. * Stores if the update was instructed by the user.
  1002. */
  1003. manualUpdate: false,
  1004.  
  1005. /**
  1006. * Init the Updater.
  1007. */
  1008. init: function() {
  1009. // Get the difference between now and the last check.
  1010. var lastCheck = myGM.getValue('updater_lastUpdateCheck', 0);
  1011. var millis = (new Date()).getTime();
  1012. var diff = millis - lastCheck;
  1013.  
  1014. // If the module is active and the last update is enough time before, check for updates.
  1015. if(myGM.getValue('module_updateActive', true) && diff > myGM.getValue('updater_updateInterval', 3600) * 1000) {
  1016. // No manual Update.
  1017. this.manualUpdate = false;
  1018.  
  1019. // Check for Updates.
  1020. this.checkForUpdates();
  1021.  
  1022. // Set the time for the last update check to now.
  1023. myGM.setValue('updater_lastUpdateCheck', millis + '');
  1024. }
  1025. },
  1026.  
  1027. /**
  1028. * Search manually for updates.
  1029. */
  1030. doManualUpdate: function() {
  1031. // Manual Update.
  1032. Updater.manualUpdate = true;
  1033.  
  1034. // Check for Updates.
  1035. Updater.checkForUpdates();
  1036.  
  1037. // Set the time for the last update check to now.
  1038. myGM.setValue('updater_lastUpdateCheck', (new Date()).getTime() + '');
  1039. },
  1040.  
  1041. /**
  1042. * Check for updates for the Script.
  1043. *
  1044. * @return boolean
  1045. * If there is a newer version.
  1046. */
  1047. checkForUpdates: function() {
  1048. // Send a request to the userscripts.org server to get the metadata of the script to check if there is a new Update.
  1049. myGM.xhr({
  1050. method: 'GET',
  1051. url: 'https://greasyfork.org/scripts/' + scriptInfo.id + '/code.meta.js',
  1052. headers: {'User-agent': 'Mozilla/5.0', 'Accept': 'text/html'},
  1053. onload: function(response) {
  1054. // Extract the metadata from the response.
  1055. var metadata = Updater.formatMetadata(response.responseText);
  1056. // If a new Update is available and the update hint should be shown.
  1057. if(Updater.newerVersion(scriptInfo.version, metadata.version2) && (myGM.getValue('updater_hideUpdate', scriptInfo.version) != metadata.version2 || Updater.manualUpdate)) {
  1058. // Show update dialogue.
  1059. Updater.showUpdateInfo(metadata);
  1060.  
  1061. // If there is no new update and it was a manual update show hint.
  1062. } else if(Updater.manualUpdate) {
  1063. // Set the notification text.
  1064. var notificationText = {
  1065. header: Language.$('default_update_noNewExists_header'),
  1066. body: Language.$('default_update_noNewExists_text1') + '<a href="http://userscripts.org/scripts/show/' + scriptInfo.id + '" target="_blank" >' + scriptInfo.name + '</a>' + Language.$('default_update_noNewExists_text2') + scriptInfo.version + Language.$('default_update_noNewExists_text3')
  1067. };
  1068.  
  1069. // Show a notification.
  1070. myGM.notification(notificationText);
  1071. }
  1072. }
  1073. });
  1074. },
  1075.  
  1076. /**
  1077. * Show the update information panel.
  1078. *
  1079. * @param String versionOld
  1080. * The old version number.
  1081. * @param String versionNew
  1082. * The new version number.
  1083. * @param int maxPartsToCompare
  1084. * The number of parts to compare at most. (optional, default "compare all parts")
  1085. *
  1086. * @return boolean
  1087. * If a new version is available.
  1088. */
  1089. newerVersion: function(versionOld, versionNew, maxPartsToCompare) {
  1090. // Stores if a new version is available.
  1091. var newVersion = false;
  1092.  
  1093. // Force both versions to be a string.
  1094. versionOld += '';
  1095. versionNew += '';
  1096.  
  1097. // The parts of the versions.
  1098. var versionOldParts = versionOld.split('.');
  1099. var versionNewParts = versionNew.split('.');
  1100.  
  1101. // The bigger number of parts of the versions.
  1102. var biggerNumberOfParts = versionOldParts.length > versionNewParts.length ? versionOldParts.length : versionNewParts.length;
  1103.  
  1104. // If all parts should be compared, set maxPartsToCompare to all parts.
  1105. if(!maxPartsToCompare || maxPartsToCompare < 1) {
  1106. maxPartsToCompare = biggerNumberOfParts + 1;
  1107. }
  1108.  
  1109. // Loop over all parts of the version with less parts.
  1110. for(var i = 0; i < biggerNumberOfParts; i++) {
  1111. // Get the value of the parts.
  1112. var versionPartOld = parseInt(versionOldParts[i] || 0);
  1113. var versionPartNew = parseInt(versionNewParts[i] || 0);
  1114.  
  1115. // If the old part is smaller than the new, return true.
  1116. if(versionPartOld < versionPartNew) {
  1117. newVersion = true;
  1118. break;
  1119.  
  1120. // Else if the old part is bigger than the new it is now new version; return false.
  1121. } else if(versionPartOld > versionPartNew || i == maxPartsToCompare - 1) {
  1122. newVersion = false;
  1123. break;
  1124. }
  1125. }
  1126.  
  1127. // No new version, return false.
  1128. return newVersion;
  1129. },
  1130.  
  1131. /**
  1132. * Show the update information panel.
  1133. *
  1134. * @param mixed[] metadata
  1135. * Array with formated metadata
  1136. */
  1137. showUpdateInfo: function(metadata) {
  1138. // Get the update history.
  1139. var updateHistory = this.extractUpdateHistory(metadata);
  1140.  
  1141. // Set the notification text.
  1142. var notificationText = {
  1143. header: Language.$('default_update_possible_header'),
  1144. bodyTop: Language.$('default_update_possible_text1') + '<a href="http://userscripts.org/scripts/show/' + scriptInfo.id + '" target="_blank" >' + scriptInfo.name + '</a>' + Language.$('default_update_possible_text2') + '.<br>' + Language.$('default_update_possible_text3') + scriptInfo.version + Language.$('default_update_possible_text4') + metadata.version + '.<br>&nbsp;&nbsp;<b><u>' + Language.$('default_update_possible_history') + '</u></b>',
  1145. bodyBottom: this.formatUpdateHistory(updateHistory),
  1146. confirm: Language.$('default_update_possible_button_install'),
  1147. abort: Language.$('default_update_possible_button_hide')
  1148. };
  1149.  
  1150. // Set the notification callback.
  1151. var notificationCallback = {
  1152. confirm: function() { win.top.location.href = 'http://userscripts.org/scripts/source/' + scriptInfo.id + '.user.js'; },
  1153. abort: function() { myGM.setValue('updater_hideUpdate', metadata.version + ''); }
  1154. };
  1155.  
  1156. // Show a notification.
  1157. myGM.notification(notificationText, notificationCallback);
  1158. },
  1159.  
  1160. /**
  1161. * Format the given metadata.
  1162. *
  1163. * @param String metadata
  1164. * The metadata to format.
  1165. *
  1166. * @return String[]
  1167. * The formated metadata as array.
  1168. */
  1169. formatMetadata: function(metadataIn) {
  1170. // Create an array for the formated metadata.
  1171. var metadataOut = new Array();
  1172.  
  1173. // Extract the tags from the metadata.
  1174. var innerMeta = metadataIn.match(/\/\/ ==UserScript==((.|\n|\r)*?)\/\/ ==\/UserScript==/)[0];
  1175.  
  1176. // If there are some tags.
  1177. if(innerMeta) {
  1178. // Extract all tags.
  1179. var tags = innerMeta.match(/\/\/ @(.*?)(\n|\r)/g);
  1180.  
  1181. // Loop over all tags.
  1182. for(var i = 0; i < tags.length; i++) {
  1183. // Extract the data from the tag.
  1184. var tmp = tags[i].match(/\/\/ @(.*?)\s+(.*)/);
  1185.  
  1186. // If there is no data with this tag create a new array to store all data with this tag.
  1187. if(!metadataOut[tmp[1]]) {
  1188. metadataOut[tmp[1]] = new Array(tmp[2]);
  1189.  
  1190. // Otherwise add the data to the existing array.
  1191. } else {
  1192. metadataOut[tmp[1]].push(tmp[2]);
  1193. }
  1194. }
  1195. }
  1196.  
  1197. // Return the formated metadata.
  1198. return metadataOut;
  1199. },
  1200.  
  1201. /**
  1202. * Extract the update history from the metadata.
  1203. *
  1204. * @param String[] metadata
  1205. * Array with the formated metadata.
  1206. *
  1207. * @return mixed[]
  1208. * The extracted update history.
  1209. */
  1210. extractUpdateHistory: function(metadata) {
  1211. // Create variable to store the update history.
  1212. var updateHistory = new Array();
  1213.  
  1214. // Loop over all update history data.
  1215. for(var i = 0; i < metadata['history'].length; i++) {
  1216. // Get the information from the update history data.
  1217. var tmp = metadata['history'][i].match(/^(\S+)\s+(\S+)\s+(.*)$/);
  1218.  
  1219. // If there is no array for this version create one.
  1220. if(!updateHistory[tmp[1]]) {
  1221. updateHistory[tmp[1]] = new Array();
  1222. }
  1223.  
  1224. // If it is a feature store it to feature in this version.
  1225. if(tmp[2] == 'Feature:') {
  1226. if(!updateHistory[tmp[1]]['feature']) {
  1227. updateHistory[tmp[1]]['feature'] = new Array(tmp[3]);
  1228. } else {
  1229. updateHistory[tmp[1]]['feature'].push(tmp[3]);
  1230. }
  1231.  
  1232. // If it is a bugfix store it to bugfix in this version.
  1233. } else if(tmp[2] == 'Bugfix:') {
  1234. if(!updateHistory[tmp[1]]['bugfix']) {
  1235. updateHistory[tmp[1]]['bugfix'] = new Array(tmp[3]);
  1236. } else {
  1237. updateHistory[tmp[1]]['bugfix'].push(tmp[3]);
  1238. }
  1239.  
  1240. // Otherwise store it to other in this version.
  1241. } else {
  1242. if(!updateHistory[tmp[1]]['other']) {
  1243. updateHistory[tmp[1]]['other'] = new Array(tmp[2] + " " + tmp[3]);
  1244. } else {
  1245. updateHistory[tmp[1]]['other'].push(tmp[2] + " " + tmp[3]);
  1246. }
  1247. }
  1248. }
  1249.  
  1250. // Return the update history.
  1251. return updateHistory;
  1252. },
  1253.  
  1254. /**
  1255. * Format the update history.
  1256. *
  1257. * @param mixed[] updateHistory
  1258. * The update history.
  1259. *
  1260. * @return String
  1261. * The formated update history.
  1262. */
  1263. formatUpdateHistory: function(updateHistory) {
  1264. // Get the labels for the types.
  1265. var types = {
  1266. feature: Language.$('default_update_possible_type_feature'),
  1267. bugfix: Language.$('default_update_possible_type_bugfix'),
  1268. other: Language.$('default_update_possible_type_other')
  1269. };
  1270.  
  1271. // Create a var for the formated update history.
  1272. var formatedUpdateHistory = '';
  1273.  
  1274. // Loop over all versions.
  1275. for(var version in updateHistory) {
  1276. // Create a headline for each version and start a table.
  1277. formatedUpdateHistory += '<h2>v ' + version + '</h2><br><table class="' + myGM.prefix + 'updateTable"><tbody>';
  1278.  
  1279. // Loop over all types.
  1280. for(var type in updateHistory[version]) {
  1281. // Create a table row for each type and start a list for the elements.
  1282. formatedUpdateHistory += '<tr><td class="' + myGM.prefix + 'updateDataType">' + types[type] + '</td><td class="' + myGM.prefix + 'updateDataInfo"><ul>';
  1283.  
  1284. // Loop over the elements and add them to the list.
  1285. for(var i = 0 ; i < updateHistory[version][type].length; i++) {
  1286. formatedUpdateHistory += '<li>' + updateHistory[version][type][i] + '</li>';
  1287. }
  1288.  
  1289. // End the list.
  1290. formatedUpdateHistory += '</ul></td></tr>';
  1291. }
  1292.  
  1293. // End the table.
  1294. formatedUpdateHistory += '</tbody></table><br>';
  1295. }
  1296.  
  1297. // Return the formated update history.
  1298. return formatedUpdateHistory;
  1299. }
  1300. };
  1301.  
  1302. /**
  1303. * Functions for language.
  1304. */
  1305. Language = {
  1306. /**
  1307. * The name of the language which is actually set.
  1308. */
  1309. name: 'English',
  1310.  
  1311. /**
  1312. * The text of the used language.
  1313. */
  1314. text: null,
  1315.  
  1316. /**
  1317. * Init the language and set the used language code.
  1318. */
  1319. init: function() {
  1320. // Get the language code.
  1321. var langCode = top.location.host.match(/^s[0-9]+-([a-zA-Z]+)\.ikariam\.gameforge\.com$/)[1];
  1322. // Set the language name.
  1323. this.setLangName(!!langCode ? langCode : 'en');
  1324.  
  1325. // Set the text in the used language.
  1326. this.setText();
  1327. },
  1328.  
  1329. /**
  1330. * Set the name of the used language.
  1331. *
  1332. * @param String code
  1333. * The laguage code.
  1334. */
  1335. setLangName: function(code) {
  1336. // All languages.
  1337. var languageName = {
  1338. ae: 'Arabic', ar: 'Spanish', ba: 'Bosnian', bg: 'Bulgarian', br: 'Portuguese', by: 'Russian',
  1339. cl: 'Spanish', cn: 'Chinese', co: 'Spanish', cz: 'Czech', de: 'German', dk: 'Danish',
  1340. ee: 'Estonian', en: 'English', es: 'Spanish', fi: 'Finish', fr: 'French', gr: 'Greek',
  1341. hk: 'Chinese', hr: 'Bosnian', hu: 'Hungarian', id: 'Indonesian', il: 'Hebrew', it: 'Italian',
  1342. kr: 'Korean', lt: 'Lithuanian', lv: 'Latvian', mx: 'Spanish', nl: 'Dutch', no: 'Norwegian',
  1343. pe: 'Spanish', ph: 'Filipino', pk: 'Urdu', pl: 'Polish', pt: 'Portuguese', ro: 'Romanian',
  1344. rs: 'Serbian', ru: 'Russian', se: 'Swedish', si: 'Slovene', sk: 'Slovak', tr: 'Turkish',
  1345. tw: 'Chinese', ua: 'Ukranian', us: 'English', ve: 'Spanish', vn: 'Vietnamese', yu: 'Bosnian'
  1346. }[code];
  1347.  
  1348. // Look up if implemented contains the language.
  1349. for(var i = 0; i < languageInfo.implemented.length; i++) {
  1350. // If the language is implemented set the name to it and return.
  1351. if(languageInfo.implemented[i] == languageName) {
  1352. this.name = languageName;
  1353. return;
  1354. }
  1355. }
  1356.  
  1357. // If the language is not implemented, set the language to english.
  1358. this.name = 'English';
  1359. },
  1360.  
  1361. /*
  1362. * Set the text for the script.
  1363. */
  1364. setText: function() {
  1365. // If a resource is used for the language text, use it.
  1366. if(languageInfo.useResource) {
  1367. // Get the ressource.
  1368. var text = myGM.getResourceParsed('language' + this.name);
  1369.  
  1370. // Store it to Language.text.
  1371. this.text = (text && !text.is_error) ? text : languageInfo.defaultText;
  1372.  
  1373. // Otherwise: Use the text in languageInfo.
  1374. } else {
  1375. // Get the text.
  1376. var text = languageInfo['text'][this.name];
  1377.  
  1378. // Store it to Language.text.
  1379. this.text = (text && !text.is_error) ? text : languageInfo.text.English;
  1380. }
  1381. },
  1382.  
  1383. /**
  1384. * Return the name of the actually used language.
  1385. *
  1386. * @return String
  1387. * The country code.
  1388. */
  1389. getLangName: function() {
  1390. return this.name;
  1391. },
  1392.  
  1393. /**
  1394. * Synonymous function for Language.getText().
  1395. *
  1396. * @param String name
  1397. * The name of the placeholder.
  1398. *
  1399. * @return mixed
  1400. * The text.
  1401. */
  1402. $: function(name) {
  1403. return this.getText(name);
  1404. },
  1405.  
  1406. /**
  1407. * Return the name of the actually used language.
  1408. *
  1409. * @param String name
  1410. * The name of the placeholder.
  1411. *
  1412. * @return mixed
  1413. * The text.
  1414. */
  1415. getText: function(name) {
  1416. // Set the text to the placeholder.
  1417. var erg = name;
  1418.  
  1419. // Split the placeholder.
  1420. var parts = name.split('_');
  1421.  
  1422. // If the splitting was successful.
  1423. if(parts) {
  1424. // Set txt to the "next level".
  1425. var txt = this.text ? this.text[parts[0]] : null;
  1426.  
  1427. // Loop over all parts.
  1428. for(var i = 1; i < parts.length; i++) {
  1429. // If the "next level" exists, set txt to it.
  1430. if(txt && typeof txt[parts[i]] != 'undefined') {
  1431. txt = txt[parts[i]];
  1432. } else {
  1433. txt = erg;
  1434. break;
  1435. }
  1436. }
  1437.  
  1438. // If the text type is not an object, a function or undefined.
  1439. if(typeof txt != 'object' && typeof txt != 'function' && typeof txt != 'undefined') {
  1440. erg = txt;
  1441. }
  1442. }
  1443.  
  1444. // Return the text.
  1445. return erg;
  1446. }
  1447. };
  1448.  
  1449. /********************************************************
  1450. *********************************************************
  1451. ***** *****
  1452. ***** End of functions / variables for all Scripts. *****
  1453. ***** *****
  1454. *********************************************************
  1455. ********************************************************/
  1456.  
  1457.  
  1458.  
  1459. /**********************
  1460. *** Ikariam Script. ***
  1461. **********************/
  1462.  
  1463. /**
  1464. * Storage for the unsafeWindow.ikariam funtion.
  1465. */
  1466. var ika;
  1467.  
  1468. /**
  1469. * General functions.
  1470. */
  1471. General = {
  1472. /**
  1473. * Init the script.
  1474. */
  1475. init: function() {
  1476. // Set the general used Script styles.
  1477. this.setStyles();
  1478.  
  1479. // Set unsafeWindow.ikariam to ika for easier access.
  1480. ika = win.ikariam;
  1481.  
  1482. // Get the id of the body.
  1483. var viewId = document.body.id;
  1484.  
  1485. // Get the name of the view depending on the body id.
  1486. switch(viewId) {
  1487. case 'worldmap_iso':
  1488. View.name = 'world';
  1489. break;
  1490.  
  1491. case 'island':
  1492. View.name = 'island';
  1493. break;
  1494.  
  1495. case 'city':
  1496. View.name = 'town';
  1497. break;
  1498.  
  1499. default:
  1500. break;
  1501. }
  1502.  
  1503. // Add the script toolbar.
  1504. myGM.addElement('div', myGM.$('#GF_toolbar'), 'toolbar');
  1505. },
  1506.  
  1507. /**
  1508. * Set the general script styles.
  1509. */
  1510. setStyles: function() {
  1511. // Add the general used styles.
  1512. myGM.addStyle(
  1513. "#" + myGM.prefix + "toolbar { position: absolute; top: 0px; right: 0px; } \
  1514. .bottomLine { border-bottom: 1px dotted #CCA569; } \
  1515. .minimizeImg, .maximizeImg { background: url('skin/interface/window_control_sprite.png') no-repeat scroll 0 0 transparent; cursor: pointer; display: block; height: 18px; width: 18px; } \
  1516. .minimizeImg { background-position: -144px 0; } \
  1517. .minimizeImg:hover { background-position: -144px -19px; } \
  1518. .maximizeImg { background-position: -126px 0; } \
  1519. .maximizeImg:hover { background-position: -126px -19px; }"
  1520. );
  1521.  
  1522. // Set the notification style.
  1523. myGM.addStyle(
  1524. "." + myGM.prefix + "notificationBackground { z-index: 1000000000000; position: fixed; visibility: visible; top: 0px; left: 0px; width: 100%; height: 100%; padding: 0; background-color: #000; opacity: .7; } \
  1525. ." + myGM.prefix + "notificationPanelContainer { z-index: 1000000000001; position: fixed; visibility: visible; top: 100px; left: 50%; width: 500px; height: 370px; margin-left: -250px; padding: 0; text-align: left; color: #542C0F; font: 12px Arial,Helvetica,sans-serif; } \
  1526. ." + myGM.prefix + "notificationPanel { position: relative; top: 0px; left: 0px; background-color: transparent; border: 0 none; overflow: hidden; } \
  1527. ." + myGM.prefix + "notificationPanelHeader { height: 39px; background: none repeat scroll 0 0 transparent; font-weight: bold; line-height: 2; white-space: nowrap; } \
  1528. ." + myGM.prefix + "notificationPanelHeaderL { height: 39px; background-image: url('skin/layout/notes_top_left.png'); background-position: left top; background-repeat: no-repeat; } \
  1529. ." + myGM.prefix + "notificationPanelHeaderR { height: 39px; background-image: url('skin/layout/notes_top_right.png'); background-position: right top; background-repeat: no-repeat; } \
  1530. ." + myGM.prefix + "notificationPanelHeaderM { height: 39px; margin: 0 14px 0 38px; padding: 12px 0 0; background-image: url('skin/layout/notes_top.png'); background-position: left top; background-repeat: repeat-x; color: #811709; line-height: 1.34em; } \
  1531. ." + myGM.prefix + "notificationPanelBody { max-height: 311px; height: 100%; background: none repeat scroll 0 0 transparent; } \
  1532. ." + myGM.prefix + "notificationPanelBodyL { height: 100%; background-image: url('skin/layout/notes_left.png'); background-position: left top; background-repeat: repeat-y; } \
  1533. ." + myGM.prefix + "notificationPanelBodyR { height: 100%; background-image: url('skin/layout/notes_right.png'); background-position: right top; background-repeat: repeat-y; } \
  1534. ." + myGM.prefix + "notificationPanelBodyM { height: 100%; background-color: #F7E7C5; background-image: none; margin: 0 6px; padding: 0 10px; font-size: 14px; } \
  1535. ." + myGM.prefix + "notificationPanelBodyMTop { max-height: 100px; line-height: 2; } \
  1536. ." + myGM.prefix + "notificationPanelBodyMTop b { line-height: 3.5; font-size:110%; } \
  1537. ." + myGM.prefix + "notificationPanelBodyM a { color: #811709; font-weight: bold; } \
  1538. ." + myGM.prefix + "notificationPanelBodyM h2 { font-weight: bold; } \
  1539. ." + myGM.prefix + "notificationPanelBodyMContent { max-height: 270px; padding: 10px; background: url('skin/input/textfield.png') repeat-x scroll 0 0 #FFF7E1; border: 1px dotted #C0C0C0; font: 14px Arial,Helvetica,sans-serif; color: #000000; border-collapse: separate; overflow-y:auto; } \
  1540. ." + myGM.prefix + "notificationPanelBodyMBottom { max-height: 170px; padding: 10px; background: url('skin/input/textfield.png') repeat-x scroll 0 0 #FFF7E1; border: 1px dotted #C0C0C0; font: 14px Arial,Helvetica,sans-serif; color: #000000; border-collapse: separate; overflow-y:auto; } \
  1541. ." + myGM.prefix + "notificationPanelBodyPlaceholder { height: 20px; } \
  1542. ." + myGM.prefix + "notificationPanelFooter { height: 20px; background: none repeat scroll 0 0 transparent; } \
  1543. ." + myGM.prefix + "notificationPanelFooterL { height: 100%; background-image: url('skin/layout/notes_left.png'); background-position: left top; background-repeat: repeat-y; border: 0 none; } \
  1544. ." + myGM.prefix + "notificationPanelFooterR { height: 21px; background-image: url('skin/layout/notes_br.png'); background-position: right bottom; background-repeat: no-repeat; } \
  1545. ." + myGM.prefix + "notificationPanelFooterM { background-color: #F7E7C5; border-bottom: 3px solid #D2A860; border-left: 2px solid #D2A860; margin: 0 23px 0 3px; padding: 3px 0 2px 3px; font-size: 77%; } \
  1546. ." + myGM.prefix + "notificationPanelClose { cursor: pointer; position: absolute; top: 12px; right: 8px; width: 17px; height: 17px; background-image: url('skin/layout/notes_close.png'); } \
  1547. ." + myGM.prefix + "notificationPanelButtonWrapper { bottom: -4px; position: absolute; margin: 10px auto; width: 100%; text-align: center; } \
  1548. ." + myGM.prefix + "notificationPanelButton { background: url('skin/input/button.png') repeat-x scroll 0 0 #ECCF8E; border-color: #C9A584 #5D4C2F #5D4C2F #C9A584; border-style: double; border-width: 3px; cursor: pointer; display: inline; font-weight: bold; margin: 0px 5px; padding: 2px 10px; text-align: center; font-size: 12px; width: 100px; } \
  1549. ." + myGM.prefix + "notificationPanelButton:hover { color: #B3713F; } \
  1550. ." + myGM.prefix + "notificationPanelButton:active { border-color: #5D4C2F #C9A584 #C9A584 #5D4C2F; border-style: double; border-width: 3px; padding: 3px 10px 1px; } \
  1551. ." + myGM.prefix + "notificationPanelButtonConfirm { } \
  1552. ." + myGM.prefix + "notificationPanelButtonAbort { }",
  1553. 'notification', true
  1554. );
  1555.  
  1556. // Set the updater style.
  1557. myGM.addStyle(
  1558. "." + myGM.prefix + "updateTable { border-collapse: separate; border-spacing: 2px; } \
  1559. ." + myGM.prefix + "updateDataType { width: 100px; padding: 5px 0px 5px 5px; border: 1px solid #D2A860; } \
  1560. ." + myGM.prefix + "updateDataInfo { width: 300px; padding: 5px 5px 5px 20px; border: 1px solid #D2A860; } \
  1561. ." + myGM.prefix + "updateDataInfo ul li { list-style: disc outside none; }",
  1562. 'updater', true
  1563. );
  1564. },
  1565.  
  1566. /**
  1567. * Parses a string number to an int value.
  1568. *
  1569. * @param String txt
  1570. * The number to format.
  1571. *
  1572. * @return int
  1573. * The formated value.
  1574. */
  1575. getInt: function(txt) {
  1576. // Return the formated number.
  1577. return parseInt(txt.replace(/(\.|,)/g, ''));
  1578. },
  1579.  
  1580. /**
  1581. * Returns the value of the selected option of a select field.
  1582. *
  1583. * @param String id
  1584. * The last part of the id of the element.
  1585. * @param boolean hasNoPrefix
  1586. * Says if the id has no prefix.
  1587. *
  1588. * @return String
  1589. * The value.
  1590. */
  1591. getSelectValue: function(id, hasNoPrefix) {
  1592. // Get the select field.
  1593. var select = myGM.$('#' + (hasNoPrefix ? '' : myGM.prefix) + id);
  1594.  
  1595. // Return the value.
  1596. return select.options[select.selectedIndex].value;
  1597. },
  1598.  
  1599. /**
  1600. * Returns a code consisting of the server name and the country code.
  1601. *
  1602. * @return string
  1603. * The code.
  1604. */
  1605. getServerCode: function() {
  1606. // Split the host string.
  1607. var lang = top.location.host.split('.');
  1608.  
  1609. // Set the language name.
  1610. return (lang ? lang[1] + '_' + lang[0] : 'undefined');
  1611. },
  1612.  
  1613. /**
  1614. * Formats a number to that format that is used in Ikariam.
  1615. *
  1616. * @param int num
  1617. * The number to format.
  1618. * @param boolean || boolean[] addColor
  1619. * If the number should be coloured. (optional, if not set, a color will be used for negative and no color will be used for positive numbers)
  1620. *
  1621. * @return String
  1622. * The formated number.
  1623. */
  1624. formatToIkaNumber: function(num, addColor, usePlusSign) {
  1625. var txt = num + '';
  1626.  
  1627. // Set a seperator every 3 digits from the end.
  1628. txt = txt.replace(/(\d)(?=(\d{3})+\b)/g, '$1' + Language.$('settings_kiloSep'));
  1629.  
  1630. // If the number is negative and it is enabled, write it in red.
  1631. if(num < 0 && !(addColor == false || (addColor && addColor.negative == false))) {
  1632. txt = '<span class="red bold negative">' + txt + '</span>';
  1633. }
  1634.  
  1635. // If the number is positive.
  1636. if(num > 0) {
  1637. // Add the plus sign if wanted.
  1638. txt = (usePlusSign ? '+' : '') + txt;
  1639.  
  1640. // Color the text green if wanted.
  1641. if(!!(addColor == true || (addColor && addColor.positive == true))) {
  1642. txt = '<span class="green bold">' + txt + '</span>';
  1643. }
  1644. }
  1645.  
  1646. // Return the formated number.
  1647. return txt;
  1648. },
  1649.  
  1650. /**
  1651. * Shows a hint to the user.
  1652. *
  1653. * @param String located
  1654. * The location of the hint. Possible are all advisors, a clicked element or a committed element.
  1655. * @param String type
  1656. * The type of the hint. Possible is confirm, error, neutral or follow the mouse.
  1657. * @param String msgText
  1658. * The hint text.
  1659. * @param String msgBindTo
  1660. * An element the tooltip is binded (only used if located = committedElement).
  1661. * @param String msgIsMinSize
  1662. * If the message is minimized (only used if type = followMouse).
  1663. */
  1664. showTooltip: function(located, type, msgText, msgBindTo, msgIsMinSize) {
  1665. // Get the message location.
  1666. var msgLocation = -1;
  1667. switch(located) {
  1668. case 'cityAdvisor':
  1669. msgLocation = 1;
  1670. break;
  1671.  
  1672. case 'militaryAdvisor':
  1673. msgLocation = 2;
  1674. break;
  1675.  
  1676. case 'researchAdvisor':
  1677. msgLocation = 3;
  1678. break;
  1679.  
  1680. case 'diplomacyAdvisor':
  1681. msgLocation = 4;
  1682. break;
  1683.  
  1684. case 'clickedElement':
  1685. msgLocation = 5;
  1686. break;
  1687.  
  1688. case 'committedElement':
  1689. msgLocation = 6;
  1690. break;
  1691. }
  1692.  
  1693. // Get the message type.
  1694. var msgType = -1;
  1695. switch(type) {
  1696. case 'confirm':
  1697. msgType = 10;
  1698. break;
  1699.  
  1700. case 'error':
  1701. msgType = 11;
  1702. break;
  1703.  
  1704. case 'neutral':
  1705. msgType = 12;
  1706. break;
  1707.  
  1708. case 'followMouse':
  1709. msgType = 13;
  1710. break;
  1711. }
  1712.  
  1713. // Show the tooltip.
  1714. ika.controller.tooltipController.bindBubbleTip(msgLocation, msgType, msgText, null, msgBindTo, msgIsMinSize);
  1715. },
  1716. /**
  1717. * Toogle the show / hide Button image and title.
  1718. *
  1719. * @param Element button
  1720. * The button to toggle.
  1721. */
  1722. toggleShowHideButton: function(button) {
  1723. // Switch the button picture.
  1724. button.classList.toggle('minimizeImg');
  1725. button.classList.toggle('maximizeImg');
  1726. // Switch the button title.
  1727. if(button.title == Language.$('general_hide')) {
  1728. button.title = Language.$('general_show');
  1729. } else {
  1730. button.title = Language.$('general_hide');
  1731. }
  1732. }
  1733. };
  1734.  
  1735. /**
  1736. * Functions for event handling.
  1737. */
  1738. EventHandling = {
  1739. /**
  1740. * Events for the upkeep reduction tables.
  1741. */
  1742. upkeepReductionTable: {
  1743. /**
  1744. * Toggles the visibility of the reduction information rows.
  1745. */
  1746. toggle: function(e) {
  1747. // Toggle the button.
  1748. General.toggleShowHideButton(this);
  1749. // Get the table rows.
  1750. var tr = myGM.$$('tr', this.parentNode.parentNode.parentNode);
  1751. // Toggle the visibility of all table rows except the first.
  1752. for(var i = 1; i < tr.length; i++) {
  1753. tr[i].classList.toggle('invisible');
  1754. }
  1755. // Adjust the size of the Scrollbar.
  1756. ika.controller.adjustSizes();
  1757. }
  1758. },
  1759.  
  1760. /**
  1761. * Events for the zoom function.
  1762. */
  1763. zoomFunction: {
  1764. /**
  1765. * Zoom in when clicking on the zoom in button.
  1766. */
  1767. zoomIn: function() {
  1768. // Get the zoom factor.
  1769. var factor = myGM.getValue('zoom_' + View.name + 'Factor', 100) + ZoomFunction.zoomStep;
  1770.  
  1771. // Zoom.
  1772. ZoomFunction.zoom(factor);
  1773. },
  1774.  
  1775. /**
  1776. * Zoom out when clicking on the zoom out button.
  1777. */
  1778. zoomOut: function() {
  1779. // Get the zoom factor.
  1780. var factor = myGM.getValue('zoom_' + View.name + 'Factor', 100) - ZoomFunction.zoomStep;
  1781.  
  1782. // Zoom.
  1783. ZoomFunction.zoom(factor);
  1784. },
  1785.  
  1786. /**
  1787. * Zoom if the mouse is scrolled.
  1788. */
  1789. mouseScroll: function(e) {
  1790. // Check if the required keys are pressed.
  1791. var keysPressed = myGM.getValue('zoom_ctrlPressed', true) ? !!e.ctrlKey : true
  1792. && myGM.getValue('zoom_altPressed', false) ? !!e.altKey : true
  1793. && myGM.getValue('zoom_shiftPressed', false) ? !!e.shiftKey : true;
  1794.  
  1795. // If the required keys are pressed.
  1796. if(keysPressed) {
  1797. // If the scrolling is horizontally return.
  1798. if (e.axis !== undefined && e.axis === e.HORIZONTAL_AXIS) {
  1799. return;
  1800. }
  1801.  
  1802. // Strorage for the number of steps to scroll.
  1803. var stepNumber = 0;
  1804.  
  1805. // Get the number of steps to scroll.
  1806. if (e.wheelDelta) {
  1807. stepNumber = e.wheelDelta / 120;
  1808. }
  1809.  
  1810. if (e.detail) {
  1811. stepNumber = -e.detail / 3;
  1812. }
  1813.  
  1814. if (e.wheelDeltaY !== undefined) {
  1815. stepNumber = e.wheelDeltaY / 120;
  1816. }
  1817. // If the number is between -1 and 0, set it to -1.
  1818. if(stepNumber < 0) {
  1819. stepNumber = stepNumber > -1 ? -1 : Math.round(stepNumber);
  1820.  
  1821. // If the number is between 0 and 1, set it to 1.
  1822. } else {
  1823. stepNumber = stepNumber < 1 ? 1 : Math.round(stepNumber);
  1824. }
  1825. // Get the zoom factor.
  1826. var factor = myGM.getValue('zoom_' + View.name + 'Factor', 100) + ZoomFunction.zoomStep * stepNumber;
  1827. // Zoom the view.
  1828. ZoomFunction.zoom(factor);
  1829.  
  1830. // Stop the default event.
  1831. if(e.preventDefault) {
  1832. e.preventDefault();
  1833. } else {
  1834. return false;
  1835. }
  1836. }
  1837. }
  1838. },
  1839.  
  1840. /**
  1841. * Events for the option panel.
  1842. */
  1843. optionPanel: {
  1844. /**
  1845. * Toggles the visibility of the option wrapper contents.
  1846. */
  1847. toggle: function(e) {
  1848. // Toggle the button.
  1849. General.toggleShowHideButton(this);
  1850. // Toggle the visibility of the content.
  1851. myGM.$('.content', this.parentNode.parentNode).classList.toggle('invisible');
  1852. // Store the visibility.
  1853. var optionId = this.parentNode.parentNode.id.replace(myGM.prefix, '');
  1854. OptionPanel.optionVisibility[optionId] = !OptionPanel.optionVisibility[optionId];
  1855. myGM.setValue('optionPanel_optionVisibility', OptionPanel.optionVisibility);
  1856.  
  1857. // Adjust the size of the Scrollbar.
  1858. ika.controller.adjustSizes();
  1859. },
  1860.  
  1861. /**
  1862. * Save the settings in the option panel.
  1863. */
  1864. saveSettings: function() {
  1865. OptionPanel.saveSettings();
  1866. }
  1867. },
  1868.  
  1869. /**
  1870. * Events for the info link.
  1871. */
  1872. memberInfo: {
  1873. /**
  1874. * Is called after the info link is clicked.
  1875. */
  1876. clickShow: function() {
  1877. // Set the flag showing the link was klicked.
  1878. myGM.setValue('memberInfo_infoLinkClicked', true);
  1879.  
  1880. // Set the search settings so that the needed view is shown.
  1881. myGM.$('#tab_highscore input[name="searchUser"]').value = '';
  1882. myGM.$('#searchOnlyFriends').checked = false;
  1883. myGM.$('#searchOnlyAllies').checked = true;
  1884.  
  1885. // Start the search.
  1886. myGM.$('#tab_highscore input[type="submit"]').click();
  1887. },
  1888.  
  1889. /**
  1890. * Is called after the reset button is clicked.
  1891. */
  1892. clickReset: function() {
  1893. // Store the member information and the actual time.
  1894. myGM.setValue(General.getServerCode() + '_memberInfo_data_' + MemberInfo.type, MemberInfo.data);
  1895. myGM.setValue(General.getServerCode() + '_memberInfo_time_' + MemberInfo.type, (new Date).getTime());
  1896.  
  1897. // Update the view.
  1898. EventHandling.memberInfo.clickShow();
  1899. }
  1900. },
  1901.  
  1902. /**
  1903. * Events for missing resources.
  1904. */
  1905. missingResources: {
  1906. /**
  1907. * If the resources are updated.
  1908. */
  1909. resourcesUpdated: function(e) {
  1910. // If the popup is closed, remove the action listener.
  1911. if(!myGM.$('#buildingUpgrade')) {
  1912. myGM.$('#cityResources').removeEventListener('DOMSubtreeModified', EventHandling.missingResources.resourcesUpdated, false);
  1913.  
  1914. // Otherwise: update the resources if necessary.
  1915. } else {
  1916. // Timeout to have access to GM_ funtions.
  1917. setTimeout(function() { MissingResources.show(true, true); }, 0);
  1918. }
  1919. }
  1920. },
  1921. /**
  1922. * Functions for replaced urls.
  1923. */
  1924. replacedUrl: {
  1925. /**
  1926. * Is called after a replaced url is clicked.
  1927. */
  1928. click: function(e) {
  1929. // The link which should be opened.
  1930. var linkToOpen = this.innerHTML.decodeHTML();
  1931. // Set the notification text.
  1932. var notificationText = {
  1933. header: Language.$('replacedUrl_notification_header'),
  1934. body: Language.$('replacedUrl_notification_text1') + '<span class="bold red">"' + linkToOpen + '"</span>' + Language.$('replacedUrl_notification_text2'),
  1935. confirm: Language.$('general_yes'),
  1936. abort: Language.$('general_no')
  1937. };
  1938.  
  1939. // Set the notification callback.
  1940. var notificationCallback = {
  1941. confirm: function() { win.open(linkToOpen); }
  1942. };
  1943.  
  1944. // Show the notification.
  1945. myGM.notification(notificationText, notificationCallback);
  1946. }
  1947. },
  1948. /**
  1949. * Functions for unit info.
  1950. */
  1951. unitInfo: {
  1952. /**
  1953. * Is called after the "show info" button is clicked.
  1954. */
  1955. click: function() {
  1956. UnitInfo.showPopup();
  1957. }
  1958. },
  1959. /**
  1960. * Events for loading preview.
  1961. */
  1962. loadingPreview: {
  1963. /**
  1964. * Is called after an attribute of loadingPreview was modified.
  1965. *
  1966. * @param event e
  1967. * The calling event.
  1968. */
  1969. attrModified: function(e) {
  1970. // If the attribute was changed.
  1971. if(e.attrChange == MutationEvent.MODIFICATION) {
  1972. // If the style.display is set to none.
  1973. if(e.attrName.trim() == 'style' && e.newValue.search(/display: none/i) != -1) {
  1974. // Timeout to have access to GM_ funtions.
  1975. setTimeout(EnhancedView.getPopup, 0);
  1976. }
  1977. }
  1978. }
  1979. }
  1980. };
  1981.  
  1982. /**
  1983. * Functions for enhanced view.
  1984. */
  1985. EnhancedView = {
  1986. /**
  1987. * Inits the enhanced view.
  1988. * Adds the event listener to the loadingPreview.
  1989. */
  1990. init: function() {
  1991. // Wait for a popup.
  1992. myGM.$('#loadingPreview').addEventListener('DOMAttrModified', EventHandling.loadingPreview.attrModified, false);
  1993.  
  1994. // Init parts which are not shown in popups.
  1995. this.initStatic();
  1996. },
  1997.  
  1998. /**
  1999. * Inits the modifications on the website which are not shown in popups.
  2000. */
  2001. initStatic: function() {
  2002. // Hide the Bird animation.
  2003. if(myGM.getValue('module_hideBirdsActive', true)) View.hideBirds();
  2004.  
  2005. // Init the Zoom function.
  2006. if(myGM.getValue('module_zoomActive', true)) ZoomFunction.init();
  2007.  
  2008. // Init the circular message link.
  2009. if(myGM.getValue('module_easyCircularMsgActive', true)) Message.easyCircularMessage();
  2010.  
  2011. // Init the function for showing the resource information.
  2012. if(myGM.getValue('module_resourceInfoActive', true)
  2013. || myGM.getValue('module_capacityInfoActive', true)) ResourceInfo.init();
  2014. // Move loading circle.
  2015. if(myGM.getValue('module_lcMoveActive', true)) View.moveLoadingCircle();
  2016.  
  2017. // Init the function for showing the missing resources.
  2018. if(myGM.getValue('module_missingResActive', true)) MissingResources.init();
  2019. // Init the function for easy access of some popups.
  2020. if(myGM.getValue('module_easyAccessActive', true)) ResourceInfo.addRessourceLinks();
  2021.  
  2022. // Don't center town advisor.
  2023. if(myGM.getValue('module_nctAdvisorActive', true)) View.noCenterTownAdvisor();
  2024.  
  2025. // If the military tooltip should be shown without mouseover or click, add the styles.
  2026. if(myGM.getValue('module_directMilitaryTtActive', true)) Tooltips.initDirectMilitaryTooltip();
  2027.  
  2028. // Set the colonizing links.
  2029. if(myGM.getValue('module_colonizingLinksActive', true)
  2030. && View.name == 'island') City.setColonizingLinks();
  2031.  
  2032. // Set the styles for the
  2033. if(myGM.getValue('module_replaceUrlsActive', true)) Message.setStyleForReplaceUrl();
  2034.  
  2035. // Init the function for showing the missing resources.
  2036. if(myGM.getValue('module_memberInfoActive', false)) MemberInfo.init();
  2037. },
  2038.  
  2039. /**
  2040. * Calls the script module depending on the popup.
  2041. */
  2042. getPopup: function() {
  2043. // Update resource information.
  2044. if(myGM.getValue('module_resourceInfoActive', true)) ResourceInfo.updateHourlyResourceInfo();
  2045.  
  2046. // Set the colonizing links.
  2047. if(myGM.getValue('module_colonizingLinksActive', true) && View.name == 'island') City.setColonizingLinks();
  2048.  
  2049. // If the script was already executed on this popup.
  2050. if(myGM.$('#' + myGM.prefix + 'alreadyExecutedPopup')) return;
  2051.  
  2052. // Get the popup.
  2053. var popup = myGM.$('.templateView');
  2054.  
  2055. // Get the popup id.
  2056. var popupId = popup ? popup.id.replace('_c', '') : '';
  2057.  
  2058. // If a popup exists, add the hint, that the popup script was executed.
  2059. if(popup) {
  2060. var alreadyExecuted = myGM.addElement('input', myGM.$('.mainContent', popup), 'alreadyExecutedPopup');
  2061. alreadyExecuted.type = 'hidden';
  2062. }
  2063.  
  2064. /******************************************************
  2065. *** Functions that should only run once on a popup. ***
  2066. ******************************************************/
  2067.  
  2068. // Select the modules of the script which should be executed.
  2069. switch(popupId) {
  2070. // Options popup.
  2071. case 'options':
  2072. OptionPanel.show();
  2073. break;
  2074.  
  2075. // Finance popup.
  2076. case 'finances':
  2077. if(myGM.getValue('module_incomeActive', true)) Balance.incomeOnTop();
  2078. if(myGM.getValue('module_urtShortActive', true)) Balance.shortUpkeepReductionTable();
  2079. break;
  2080.  
  2081. // Military view popup.
  2082. case 'militaryAdvisor':
  2083. if(myGM.getValue('module_directMilitaryTtActive', true)) Tooltips.directMilitaryTooltip();
  2084. if(myGM.getValue('module_ttAutoActive', true)) Tooltips.autoshowInMilitaryView();
  2085. break;
  2086.  
  2087. // Diplomacy ally view popup.
  2088. case 'diplomacyAlly':
  2089. if(myGM.getValue('module_ttAutoActive', true)) Tooltips.autoshowInAllianceView();
  2090. break;
  2091.  
  2092. // Show messages.
  2093. case 'diplomacyAdvisor':
  2094. case 'diplomacyAdvisorOutBox':
  2095. case 'diplomacyAdvisorArchive':
  2096. case 'diplomacyAdvisorArchiveOutBox':
  2097. if(myGM.getValue('module_replaceUrlsActive', true)) Message.replaceUrl();
  2098. break;
  2099.  
  2100. // Diplomacy ally view popup in embassy.
  2101. case 'embassy':
  2102. if(myGM.getValue('module_ttAutoActive', true)) Tooltips.autoshowInAllianceView();
  2103. break;
  2104.  
  2105. // Building ground popup.
  2106. case 'buildingGround':
  2107. if(myGM.getValue('module_missingResActive', true)) MissingResources.showInBuildingGround();
  2108. break;
  2109.  
  2110. // Write message popup.
  2111. case 'sendIKMessage':
  2112. if(myGM.getValue('module_messageSigActive', true)) Message.addSignature();
  2113. break;
  2114. // Troops in town popup.
  2115. case 'cityMilitary':
  2116. if(myGM.getValue('module_unitInfoActive', true)) UnitInfo.addPopupLink();
  2117. break;
  2118.  
  2119. // Highscore popup.
  2120. case 'highscore':
  2121. if(myGM.getValue('module_memberInfoActive', false)) MemberInfo.show();
  2122. break;
  2123. }
  2124.  
  2125. // Building view.
  2126. if(myGM.$('#buildingUpgrade') && myGM.getValue('module_missingResActive', true)) MissingResources.showInSidebar();
  2127. }
  2128. };
  2129.  
  2130. /**
  2131. * Functions for the general view.
  2132. */
  2133. View = {
  2134. /**
  2135. * Storage for the name of the view.
  2136. */
  2137. name: '',
  2138.  
  2139. /**
  2140. * Move loading circle to breadcrumb.
  2141. */
  2142. moveLoadingCircle: function() {
  2143. // Add the styles.
  2144. myGM.addStyle(
  2145. "#js_worldBread { margin-left: 16px !important; } \
  2146. #loadingPreview { transform: scale(0.5); -o-transform: scale(0.5); -webkit-transform: scale(0.5); left: 35px !important; top: 141px !important; }"
  2147. );
  2148. },
  2149.  
  2150. /**
  2151. * Hide the bird animation but no other animation.
  2152. */
  2153. hideBirds: function() {
  2154. // Add the style.
  2155. myGM.addStyle(
  2156. ".bird_swarm { visibility: hidden !important; }"
  2157. );
  2158. },
  2159.  
  2160. /**
  2161. * Don't center the city and date in town advisor vertically.
  2162. */
  2163. noCenterTownAdvisor: function() {
  2164. // Add the style.
  2165. myGM.addStyle(
  2166. "#inboxCity td { vertical-align: top !important; }"
  2167. );
  2168. }
  2169. };
  2170.  
  2171. /**
  2172. * Functions for tooltips.
  2173. */
  2174. Tooltips = {
  2175. /**
  2176. * Show tooltips in alliance view automatically.
  2177. */
  2178. autoshowInAllianceView: function() {
  2179. // Enable toggling on mouseover / mouseout.
  2180. this.autoshowGeneral('cityInfo');
  2181. },
  2182.  
  2183. /**
  2184. * Show tooltips in military advisor view automatically.
  2185. */
  2186. autoshowInMilitaryView: function() {
  2187. // Enable toggling on mouseover / mouseout.
  2188. this.autoshowGeneral('spyMilitary', myGM.getValue('module_directMilitaryTtActive', true));
  2189. },
  2190.  
  2191. /**
  2192. * Show tooltips with class name magnifierClass automatically.
  2193. *
  2194. * @param String magnifierClass
  2195. * The class of the tooltips to enable toggling.
  2196. * @param boolean deleteOnClick
  2197. * If the onClick event should be deleted or just moved to mouseover.
  2198. */
  2199. autoshowGeneral: function(magnifierClass, deleteOnClick) {
  2200. // Get all magnifiers.
  2201. var magnifier = myGM.$$('.' + magnifierClass);
  2202.  
  2203. // Set the mousover and mouseout for all magnifiers.
  2204. for(var i = 0; i < magnifier.length; i++) {
  2205. // Get the onclick event and "delete" the old one.
  2206. var magOnClick = magnifier[i].onclick;
  2207. magnifier[i].onclick = 'return false;';
  2208.  
  2209. // If the on click event should be moved.
  2210. if(!deleteOnClick) {
  2211. // Add the show event.
  2212. var magIcon = myGM.$('.magnify_icon', magnifier[i]);
  2213. if(!magIcon) magIcon = magnifier[i];
  2214. magIcon.addEventListener('mouseover', function(e) { ika.controller.captureMousePosition(e); this(e); }.bind(magOnClick), true);
  2215. }
  2216. }
  2217.  
  2218. // If the on click event should be moved.
  2219. if(!deleteOnClick) {
  2220. // Trigger the close event with jQuery on a click on the popup.
  2221. myGM.$('.templateView .mainContent').addEventListener('click', function() { win.$(document).trigger("closeExclusiveInfo"); }, true);
  2222. }
  2223. },
  2224.  
  2225. /**
  2226. * Shows the military tooltip without mouseover.
  2227. */
  2228. initDirectMilitaryTooltip: function() {
  2229. // Add the styles.
  2230. myGM.addStyle(
  2231. "#js_MilitaryMovementsFleetMovementsTable .military_event_table .magnify_icon { background-image: none; cursor: default; width: 240px; } \
  2232. #js_MilitaryMovementsFleetMovementsTable .military_event_table .magnify_icon .infoTip { display: inline; position: relative; padding: 0px; border: none; } \
  2233. #js_MilitaryMovementsFleetMovementsTable .military_event_table .magnify_icon .infoTip h5 { display: none; } \
  2234. #js_MilitaryMovementsFleetMovementsTable .military_event_table .icon40 { background-size: 25px 25px; background-color: transparent; padding: 26px 3px 0px 3px; width: 30px; } \
  2235. #js_MilitaryMovementsFleetMovementsTable .military_event_table .icon40.resource_icon { background-size: 20px 16px; }"
  2236. );
  2237. },
  2238.  
  2239. /**
  2240. * Hide the ship number of own transports and add titles to the loaded troops / ships / resources.
  2241. */
  2242. directMilitaryTooltip: function() {
  2243. // Get the table rows.
  2244. var militaryEventTableTr = myGM.$$('#js_MilitaryMovementsFleetMovementsTable .military_event_table tr');
  2245. // Loop at the table rows.
  2246. for(var i = 1; i < militaryEventTableTr.length; i++) {
  2247. // Get the mission div.
  2248. var missionDiv = myGM.$('td:nth-child(1) div.mission_icon', militaryEventTableTr[i]);
  2249.  
  2250. // If it is your own table row and the mission is transport or trade.
  2251. if(militaryEventTableTr[i].classList.contains('own') && (missionDiv.classList.contains('transport') || missionDiv.classList.contains('trade'))) {
  2252. // Hide the table cell.
  2253. myGM.$('td:nth-child(4) div', militaryEventTableTr[i]).classList.add('invisible');
  2254. }
  2255. }
  2256. // Storage for the unit / ships / resources classes and names.
  2257. var idTranslation = new Array(
  2258. { classId: 'swordsman', langId: 'name_unit_swordsman' }, { classId: 'phalanx', langId: 'name_unit_phalanx' }, { classId: 'archer', langId: 'name_unit_archer' }, { classId: 'marksman', langId: 'name_unit_marksman' }, { classId: 'mortar', langId: 'name_unit_mortar' }, { classId: 'slinger', langId: 'name_unit_slinger' }, { classId: 'catapult', langId: 'name_unit_catapult' }, { classId: 'ram', langId: 'name_unit_ram' }, { classId: 'steamgiant', langId: 'name_unit_steamgiant' }, { classId: 'bombardier', langId: 'name_unit_bombardier' }, { classId: 'cook', langId: 'name_unit_cook' },
  2259. { classId: 'medic', langId: 'name_unit_medic' }, { classId: 'gyrocopter', langId: 'name_unit_gyrocopter' }, { classId: 'spearman', langId: 'name_unit_spearman' }, { classId: 'ship_balliasta', langId: 'name_ship_balliasta' }, { classId: 'ship_catapult', langId: 'name_ship_catapult' }, { classId: 'ship_flamethrower', langId: 'name_ship_flamethrower' }, { classId: 'ship_mortar', langId: 'name_ship_mortar' }, { classId: 'ship_ram', langId: 'name_ship_ram' }, { classId: 'ship_steamboat', langId: 'name_ship_steamboat' }, { classId: 'ship_rocketship', langId: 'name_ship_rocketship' }, { classId: 'ship_submarine', langId: 'name_ship_submarine' },
  2260. { classId: 'ship_paddlespeedship', langId: 'name_ship_paddlespeedship' }, { classId: 'ship_balloncarrier', langId: 'name_ship_balloncarrier' }, { classId: 'ship_tender', langId: 'name_ship_tender' }, { classId: 'ship_transport', langId: 'name_ship_transport' }, { classId: 'ship_premium_transport', langId: 'name_ship_transport' }, { classId: 'gold', langId: 'name_resource_gold' }, { classId: 'wood', langId: 'name_resource_wood' }, { classId: 'wine', langId: 'name_resource_wine' }, { classId: 'marble', langId: 'name_resource_marble' }, { classId: 'glass', langId: 'name_resource_glass' }, { classId: 'sulfur', langId: 'name_resource_sulfur' }
  2261. );
  2262. // Get the event table.
  2263. var movementsTable = myGM.$('#js_MilitaryMovementsFleetMovementsTable');
  2264. // Add the unit names.
  2265. for(var i = 0; i < idTranslation.length; i++) {
  2266. var detailIcon = myGM.$$('.icon40.' + idTranslation[i].classId, movementsTable);
  2267. for(var k = 0; k < detailIcon.length; k++) {
  2268. detailIcon[k].title = Language.$(idTranslation[i].langId);
  2269. }
  2270. }
  2271. }
  2272. };
  2273.  
  2274. /**
  2275. * Functions for balance view.
  2276. */
  2277. Balance = {
  2278. /**
  2279. * Shows the actual income also on top of the site.
  2280. */
  2281. incomeOnTop: function() {
  2282. // Get the table for the summary.
  2283. var summaryTable = myGM.$('.table01');
  2284.  
  2285. // Show the income on top.
  2286. this.showIncomeOnTop(summaryTable);
  2287.  
  2288. // Adjust the size of the Scrollbar.
  2289. ika.controller.adjustSizes();
  2290. },
  2291.  
  2292. /**
  2293. * Show the actual income on top of the site.
  2294. *
  2295. * @param element summaryTable
  2296. * The table for the summary.
  2297. */
  2298. showIncomeOnTop: function(summaryTable) {
  2299. // Get the actual income.
  2300. var income = this.getIncome();
  2301.  
  2302. // Create the rows for the income per day and the income per day.
  2303. var incomeRow = myGM.addElement('tr', summaryTable, null, new Array('result', 'alt'));
  2304. var incomeRow24h = myGM.addElement('tr', summaryTable, null, 'result');
  2305.  
  2306. // Create the content of the table rows.
  2307. this.createTableRow(new Array(Language.$('balance_income_perHour'), '', '', General.formatToIkaNumber(income)), new Array('sigma', ['value', 'res'], ['value', 'res'], ['value', 'res']), incomeRow, false);
  2308. this.createTableRow(new Array(Language.$('balance_income_perDay'), '', '', General.formatToIkaNumber(income * 24)), new Array('sigma', ['value', 'res'], ['value', 'res'], ['value', 'res']), incomeRow24h, false);
  2309. },
  2310.  
  2311. /**
  2312. * Gets the actual income from the Ikariam page and returns it.
  2313. *
  2314. * @return int
  2315. * The actual income
  2316. */
  2317. getIncome: function() {
  2318. // Get the table cell with the actual income.
  2319. var incomeCell = myGM.$$('.hidden')[myGM.$$('.hidden').length - 1];
  2320.  
  2321. // If the content of the cell is not just the income move one element inwards.
  2322. while(incomeCell.firstChild.firstChild) {
  2323. incomeCell = incomeCell.firstChild;
  2324. }
  2325.  
  2326. // Get the actual income.
  2327. var txt = incomeCell.innerHTML;
  2328.  
  2329. // Remove the thousand seperators.
  2330. return General.getInt(txt);
  2331. },
  2332.  
  2333. /**
  2334. * Shows a short upkeep reduction table.
  2335. */
  2336. shortUpkeepReductionTable: function() {
  2337. // Get the upkeep redutcion tables.
  2338. var uRT = myGM.$$('.upkeepReductionTable');
  2339.  
  2340. if(uRT.length == 0) {
  2341. uRT = myGM.$$('#upkeepReductionTable');
  2342. }
  2343.  
  2344. // Create an array for data storage.
  2345. var row = {
  2346. reason: new Array(),
  2347. basicUpkeep: new Array(),
  2348. supplyUpkeep: new Array(),
  2349. result: new Array()
  2350. };
  2351. // Get the data for the troops and ships redution rows.
  2352. for(var i = 0; i < 3; i++) {
  2353. row.reason.push(Language.$('balance_upkeep_reason_' + i));
  2354. row.basicUpkeep.push(General.getInt(myGM.$$('.altbottomLine td.hidden, .result td.hidden, .alt.bottomLine td.hidden, .result td.hidden', uRT[0])[i].innerHTML));
  2355. row.supplyUpkeep.push(General.getInt(myGM.$$('.altbottomLine td.hidden, .result td.hidden, .alt.bottomLine td.hidden, .result td.hidden', uRT[1])[i].innerHTML));
  2356. row.result.push(row.basicUpkeep[i] + row.supplyUpkeep[i]);
  2357. }
  2358.  
  2359. // Get the start income.
  2360. var beforeReduction = General.getInt(myGM.$('td.hidden', uRT[2]).innerHTML);
  2361.  
  2362. // Get the result income.
  2363. var income = this.getIncome();
  2364.  
  2365. // Create the table to show the
  2366. var shortTable = myGM.addElement('table', uRT[0].parentNode, null, new Array('table01', 'border', 'left'), null, null, uRT[0]);
  2367. shortTable.id = 'balance';
  2368.  
  2369. // Create the table head.
  2370. this.createTableRow(new Array('', Language.$('balance_upkeep_basic'), Language.$('balance_upkeep_supply'), Language.$('balance_upkeep_result')), new Array('city', ['value', 'res'], ['value', 'res'], ['value', 'res']), myGM.addElement('tr', shortTable), true);
  2371.  
  2372. // Create the start income row.
  2373. var startRow = myGM.addElement('tr', shortTable, null, new Array('alt', 'bottomLine'));
  2374. this.createTableRow(new Array(Language.$('balance_income_start'), '', '', General.formatToIkaNumber(beforeReduction)), new Array('city', ['value', 'res'], ['value', 'res'], ['value', 'res']), startRow, false);
  2375.  
  2376. // Create the troops / ships redution rows.
  2377. for(var i = 0; i < 3; i++) {
  2378. var newRow = myGM.addElement('tr', shortTable, null, (i % 2 == 1) ? new Array('alt', 'bottomLine') : '');
  2379. this.createTableRow(new Array(row.reason[i], General.formatToIkaNumber(-row.basicUpkeep[i]), General.formatToIkaNumber(-row.supplyUpkeep[i]), General.formatToIkaNumber(-row.result[i])), new Array('city', ['value', 'res'], ['value', 'res'], 'hidden'), newRow, false);
  2380. }
  2381.  
  2382. // Create the result row.
  2383. var resultRow = myGM.addElement('tr', shortTable, null, 'result');
  2384. this.createTableRow(new Array('<img alt="Summe" src="skin/layout/sigma.png">', '', '', General.formatToIkaNumber(income)), new Array('sigma', ['value', 'res'], ['value', 'res'], 'hidden'), resultRow, false);
  2385.  
  2386. // Create the spacing between the tables.
  2387. myGM.addElement('hr', uRT[0].parentNode, null, null, null, null, uRT[0]);
  2388.  
  2389. // Hide the data rows of the tables and add the show button.
  2390. for(var i = 0; i < uRT.length; i++) {
  2391. // Get all rows.
  2392. var tr = myGM.$$('tr', uRT[i]);
  2393.  
  2394. // Hide all rows except the first.
  2395. for(var k = 1; k < tr.length; k++) {
  2396. tr[k].classList.add('invisible');
  2397. }
  2398.  
  2399. // Add the show button to the first row.
  2400. var th = myGM.$('th', tr[0]);
  2401. var btn = myGM.addElement('div', th, null, 'maximizeImg', new Array(['cssFloat', 'left']), th.firstChild);
  2402. btn.title = Language.$('general_show');
  2403.  
  2404. // Add the event listener.
  2405. btn.addEventListener('click', EventHandling.upkeepReductionTable.toggle, false);
  2406. }
  2407.  
  2408. // Adjust the size of the Scrollbar.
  2409. ika.controller.adjustSizes();
  2410. },
  2411.  
  2412. /**
  2413. * Adds cells to a table row.
  2414. *
  2415. * @param String[] cellText
  2416. * Array with the text of the cells.
  2417. * @param String[] cellClassName
  2418. * Array with the classes of the cells.
  2419. * @param element row
  2420. * Table row where the cells should be added.
  2421. * @param boolean head
  2422. * If the row is a table head row.
  2423. */
  2424. createTableRow: function(cellText, cellClassName, row, head) {
  2425. // Do this for every cell.
  2426. for(var i = 0; i < cellText.length; i++) {
  2427. // Add the cell.
  2428. var cell = myGM.addElement(head ? 'th' : 'td', row, null, cellClassName[i]);
  2429.  
  2430. // Set the content of the cell.
  2431. cell.innerHTML = cellText[i];
  2432. }
  2433. }
  2434. };
  2435.  
  2436. /**
  2437. * Functions for option panel.
  2438. */
  2439. OptionPanel = {
  2440. /**
  2441. * Storage for option visibility.
  2442. */
  2443. optionVisibility: {
  2444. moduleOption: true
  2445. },
  2446.  
  2447. /**
  2448. * Adds the tab for the script options.
  2449. */
  2450. show: function() {
  2451. // Get the script options tab.
  2452. var tabGMOptions = myGM.$('#tabScriptOptions');
  2453. // If the script options tab doesn't exists, create it.
  2454. if(!tabGMOptions) {
  2455. // Set the styles.
  2456. this.setStyles();
  2457.  
  2458. // Add the GM tab link to the tab menu.
  2459. var tabmenu = myGM.$('.tabmenu');
  2460. var jsTabGMOptions = myGM.addElement('li', tabmenu, 'js_tabScriptOptions', 'tab', null, false);
  2461. jsTabGMOptions.innerHTML = '<b class="tabScriptOptions"> ' + Language.$('optionPanel_scripts') + ' </b>';
  2462. jsTabGMOptions.setAttribute('onclick', "switchTab('tabScriptOptions');");
  2463.  
  2464. // Add the content wrapper for the GM tab to the tab menu.
  2465. var mainContent = myGM.$('#tabGameOptions').parentNode;
  2466. tabGMOptions = myGM.addElement('div', mainContent, 'tabScriptOptions', null, new Array(['display', 'none']), false);
  2467. }
  2468.  
  2469. // Fill the tab with content.
  2470. this.createTabContent(tabGMOptions);
  2471. },
  2472.  
  2473. /**
  2474. * Sets the styles that are used for the update-panel.
  2475. */
  2476. setStyles: function() {
  2477. // Add all styles to the ikariam page.
  2478. myGM.addStyle(
  2479. "#js_tabGameOptions, #js_tabAccountOptions, #js_tabFacebookOptions, #js_tabOpenIDOptions, #js_tabScriptOptions { width: 130px !important; margin-left: 5px !important; border-radius: 5px 5px 0px 0px } \
  2480. ." + myGM.prefix + "SignatureInput { resize: none; width: 99%; height: 75px; } \
  2481. #tabScriptOptions hr { margin: 0; } \
  2482. .cbWrapper { margin: 0 0 0 10px; }"
  2483. );
  2484. },
  2485.  
  2486. /**
  2487. * Creates the content of the tab.
  2488. *
  2489. * @param element tab
  2490. * The tab where the content should be added.
  2491. */
  2492. createTabContent: function(tab) {
  2493. // Get the option visibility.
  2494. this.optionVisibility = myGM.getValue('optionPanel_optionVisibility', this.optionVisibility);
  2495.  
  2496. // Create the wrapper for the enabling / disabling of modules.
  2497. var moduleContentWrapper = this.createOptionsWrapper(tab, Language.$('optionPanel_section_module_title'), 'moduleOption');
  2498. this.createModuleContent(moduleContentWrapper);
  2499.  
  2500. // Create the wrapper for the update settings.
  2501. var updateContentWrapper = this.createOptionsWrapper(tab, Language.$('optionPanel_section_update_title'), 'updateOption');
  2502. this.createUpdateContent(updateContentWrapper);
  2503.  
  2504. // Create the wrapper for the resource information / missing resources.
  2505. var rimrContentWrapper = this.createOptionsWrapper(tab, Language.$('optionPanel_section_resInfoMissingRes_title'), 'resInfoMissingResOption');
  2506. this.createResInfoMissingResContent(rimrContentWrapper);
  2507.  
  2508. // Create the wrapper for the zoom settings.
  2509. var zoomContentWrapper = this.createOptionsWrapper(tab, Language.$('optionPanel_section_zoom_title'), 'zoomOption');
  2510. this.createZoomContent(zoomContentWrapper);
  2511.  
  2512. // Create the wrapper for the zoom settings.
  2513. var sigContentWrapper = this.createOptionsWrapper(tab, Language.$('optionPanel_section_messageSignature_title'), 'messageSignatureOption');
  2514. this.createMessageSigContent(sigContentWrapper);
  2515. },
  2516.  
  2517. /**
  2518. * Create a wrapper for a section on the option panel.
  2519. *
  2520. * @param element tab
  2521. * The tab where the wrapper should be added.
  2522. * @param String headerText
  2523. * The text of the header.
  2524. * @param String id
  2525. * The id of the option wrapper.
  2526. *
  2527. * @return element
  2528. * The wrapper for the content of the options.
  2529. */
  2530. createOptionsWrapper: function(tab, headerText, id) {
  2531. // Get the content show status.
  2532. var showContent = !!this.optionVisibility[id];
  2533.  
  2534. // Create the wrapper.
  2535. var optionsWrapper = myGM.addElement('div', tab, id, 'contentBox01h');
  2536.  
  2537. // Create the header.
  2538. var optionsHeader = myGM.addElement('h3', optionsWrapper, null, 'header');
  2539. optionsHeader.innerHTML = headerText;
  2540.  
  2541. // Add the show / hide button.
  2542. var btn = myGM.addElement('div', optionsHeader, null, showContent ? 'minimizeImg' : 'maximizeImg', new Array(['cssFloat', 'left']));
  2543. btn.addEventListener('click', EventHandling.optionPanel.toggle, false);
  2544. btn.title = showContent ? Language.$('general_hide') : Language.$('general_show');
  2545.  
  2546. // Create the content wrapper.
  2547. var optionsWrapperContent = myGM.addElement('div', optionsWrapper, null, showContent ? 'content' : new Array('content', 'invisible'));
  2548.  
  2549. // Create the footer.
  2550. myGM.addElement('div', optionsWrapper, null, 'footer');
  2551.  
  2552. // Return the content wrapper.
  2553. return optionsWrapperContent;
  2554. },
  2555.  
  2556. /**
  2557. * Creates the content of the module part.
  2558. *
  2559. * @param element contentWrapper
  2560. * The wrapper where the content should be added.
  2561. */
  2562. createModuleContent: function(contentWrapper) {
  2563. // Create options table.
  2564. var updateTable = this.addOptionsTable(contentWrapper);
  2565.  
  2566. // Set the checkbox data.
  2567. var cbData = new Array(
  2568. { id: 'update', checked: myGM.getValue('module_updateActive', true), label: Language.$('optionPanel_section_module_label_updateActive'), hrAfter: true },
  2569. { id: 'incomeOnTop', checked: myGM.getValue('module_incomeActive', true), label: Language.$('optionPanel_section_module_label_incomeOnTopActive'), hrAfter: false },
  2570. { id: 'upkeepReduction', checked: myGM.getValue('module_urtShortActive', true), label: Language.$('optionPanel_section_module_label_upkeepReductionActive'), hrAfter: false },
  2571. { id: 'missingResources', checked: myGM.getValue('module_missingResActive', true), label: Language.$('optionPanel_section_module_label_missingResActive'), hrAfter: false },
  2572. { id: 'resourceInformation', checked: myGM.getValue('module_resourceInfoActive', true), label: Language.$('optionPanel_section_module_label_resourceInfoActive'), hrAfter: false },
  2573. { id: 'capacityInformation', checked: myGM.getValue('module_capacityInfoActive', true), label: Language.$('optionPanel_section_module_label_capacityInfoActive'), hrAfter: false },
  2574. { id: 'easyAccess', checked: myGM.getValue('module_easyAccessActive', true), label: Language.$('optionPanel_section_module_label_easyAccessActive'), hrAfter: false },
  2575. { id: 'zoom', checked: myGM.getValue('module_zoomActive', true), label: Language.$('optionPanel_section_module_label_zoomActive'), hrAfter: true },
  2576. { id: 'messageSignature', checked: myGM.getValue('module_messageSigActive', true), label: Language.$('optionPanel_section_module_label_messageSignatureActive'), hrAfter: false },
  2577. { id: 'easyCircularMessage', checked: myGM.getValue('module_easyCircularMsgActive', true), label: Language.$('optionPanel_section_module_label_easyCircularMsgActive'), hrAfter: false },
  2578. { id: 'replaceUrls', checked: myGM.getValue('module_replaceUrlsActive', true), label: Language.$('optionPanel_section_module_label_replaceUrlsActive'), hrAfter: false },
  2579. { id: 'colonizingLinks', checked: myGM.getValue('module_colonizingLinksActive', true), label: Language.$('optionPanel_section_module_label_colonizingLinksActive'), hrAfter: true },
  2580. { id: 'loadingCircleMove', checked: myGM.getValue('module_lcMoveActive', true), label: Language.$('optionPanel_section_module_label_lcMoveActive'), hrAfter: false },
  2581. { id: 'tooltipsAuto', checked: myGM.getValue('module_ttAutoActive', true), label: Language.$('optionPanel_section_module_label_tooltipsAutoActive'), hrAfter: false },
  2582. { id: 'directMilitaryTooltip', checked: myGM.getValue('module_directMilitaryTtActive', true), label: Language.$('optionPanel_section_module_label_directMilitaryTtActive'), hrAfter: false },
  2583. { id: 'unitInfo', checked: myGM.getValue('module_unitInfoActive', true), label: Language.$('optionPanel_section_module_label_unitInfoActive'), hrAfter: true },
  2584. { id: 'hideBirds', checked: myGM.getValue('module_hideBirdsActive', true), label: Language.$('optionPanel_section_module_label_hideBirdsActive'), hrAfter: false },
  2585. { id: 'noCenterTownAdvisor', checked: myGM.getValue('module_nctAdvisorActive', true), label: Language.$('optionPanel_section_module_label_nctAdvisorActive'), hrAfter: false },
  2586. { id: 'memberInformation', checked: myGM.getValue('module_memberInfoActive', false), label: Language.$('optionPanel_section_module_label_memberInfoActive'), hrAfter: false }
  2587. );
  2588.  
  2589. // Create the checkboxes.
  2590. this.addCheckboxes(updateTable, cbData);
  2591.  
  2592. // Add the button to save the settings.
  2593. this.addSaveButton(contentWrapper);
  2594. },
  2595.  
  2596. /**
  2597. * Creates the content of the update part.
  2598. *
  2599. * @param element contentWrapper
  2600. * The wrapper where the content should be added.
  2601. */
  2602. createUpdateContent: function(contentWrapper) {
  2603. // Create options table.
  2604. var updateTable = this.addOptionsTable(contentWrapper);
  2605.  
  2606. // Array for update interval values and names.
  2607. var opts = new Array(
  2608. { value: 3600, name: Language.$('optionPanel_section_update_label_interval_option_hour') },
  2609. { value: 43200, name: Language.$('optionPanel_section_update_label_interval_option_hour12') },
  2610. { value: 86400, name: Language.$('optionPanel_section_update_label_interval_option_day') },
  2611. { value: 259200, name: Language.$('optionPanel_section_update_label_interval_option_day3') },
  2612. { value: 604800, name: Language.$('optionPanel_section_update_label_interval_option_week') },
  2613. { value: 1209600, name: Language.$('optionPanel_section_update_label_interval_option_week2') },
  2614. { value: 2419200, name: Language.$('optionPanel_section_update_label_interval_option_week4') }
  2615. );
  2616.  
  2617. // Create the update interval select.
  2618. this.addSelect(updateTable, 'updateInterval', myGM.getValue('updater_updateInterval', 3600), opts, Language.$('optionPanel_section_update_label_interval_description'));
  2619.  
  2620. // Prepare the update link table row.
  2621. var updateLinkTr = this.addOptionsTableRow(updateTable, true);
  2622. updateLinkTr.firstChild.classList.add('center');
  2623. updateLinkTr.firstChild.classList.remove('left');
  2624.  
  2625. // Add the link for manual updates.
  2626. var updateLink = myGM.addElement('a', updateLinkTr.firstChild);
  2627. updateLink.href = '#';
  2628. updateLink.innerHTML = Language.$('optionPanel_section_update_label_manual_text1') + '"' + scriptInfo.name + '"' + Language.$('optionPanel_section_update_label_manual_text2');
  2629. updateLink.addEventListener('click', Updater.doManualUpdate, false);
  2630.  
  2631. // Add the button to save the settings.
  2632. this.addSaveButton(contentWrapper);
  2633. },
  2634.  
  2635. /**
  2636. * Create the content of the resource information / missing resources part.
  2637. *
  2638. * @param element contentWrapper
  2639. * The wrapper where the content should be added.
  2640. */
  2641. createResInfoMissingResContent: function(contentWrapper) {
  2642. // Create options table.
  2643. var selectionTable = this.addOptionsTable(contentWrapper);
  2644. var checkboxTable = this.addOptionsTable(contentWrapper);
  2645.  
  2646. // Array for update interval values and names.
  2647. var optsAlign = new Array(
  2648. { value: 'alignRight', name: Language.$('optionPanel_section_resInfoMissingRes_label_align_right') },
  2649. { value: 'alignLeft', name: Language.$('optionPanel_section_resInfoMissingRes_label_align_left') },
  2650. { value: 'withSeparation', name: Language.$('optionPanel_section_resInfoMissingRes_label_align_rightWithSeparation') }
  2651. );
  2652.  
  2653. // Create the hourly income style select.
  2654. this.addSelect(selectionTable, 'hourlyIncomeStyle', myGM.getValue('resourceInfo_hourlyIncomeStyle', 'alignRight'), optsAlign, Language.$('optionPanel_section_resInfoMissingRes_label_hourlyIncomeStyle'));
  2655. // Array for update interval values and names.
  2656. var optsOrientation = new Array(
  2657. { value: 'vertical', name: Language.$('optionPanel_section_resInfoMissingRes_label_orientation_vertical') },
  2658. { value: 'horizontal', name: Language.$('optionPanel_section_resInfoMissingRes_label_orientation_horizontal') },
  2659. { value: 'horizontalFull', name: Language.$('optionPanel_section_resInfoMissingRes_label_orientation_horizontalFull') }
  2660. );
  2661.  
  2662. // Create the hourly income style select.
  2663. this.addSelect(selectionTable, 'capacityInfoStyle', myGM.getValue('resourceInfo_capacityStyle_orientation', 'vertical'), optsOrientation, Language.$('optionPanel_section_resInfoMissingRes_label_capacityOrientationStyle'));
  2664. // Get the checkbox data.
  2665. var cbData = new Array(
  2666. { id: 'hasBorder', checked: myGM.getValue('resourceInfo_capacityStyle_hasBorder', true), label: Language.$('optionPanel_section_resInfoMissingRes_label_hasBorder'), hrAfter: false },
  2667. { id: 'showBranchRes', checked: myGM.getValue('resourceInfo_showBranchRes', true), label: Language.$('optionPanel_section_resInfoMissingRes_label_showBranchRes'), hrAfter: true },
  2668. { id: 'showPositive', checked: myGM.getValue('missingRes_showPositive', true), label: Language.$('optionPanel_section_resInfoMissingRes_label_showPositive'), hrAfter: false },
  2669. { id: 'showColoured', checked: myGM.getValue('missingRes_showColoured', true), label: Language.$('optionPanel_section_resInfoMissingRes_label_showColoured'), hrAfter: false }
  2670. );
  2671.  
  2672. // Create the checkboxes.
  2673. this.addCheckboxes(checkboxTable, cbData);
  2674.  
  2675. // Add the button to save the settings.
  2676. this.addSaveButton(contentWrapper);
  2677. },
  2678.  
  2679. /**
  2680. * Create the content of the zoom part.
  2681. *
  2682. * @param element contentWrapper
  2683. * The wrapper where the content should be added.
  2684. */
  2685. createZoomContent: function(contentWrapper) {
  2686. // Create the options tables.
  2687. var factorTable = this.addOptionsTable(contentWrapper);
  2688. var scaleChildrenTable = this.addOptionsTable(contentWrapper);
  2689. var accessKeysTable = this.addOptionsTable(contentWrapper);
  2690.  
  2691. // Set the description data.
  2692. var descriptionRowData = new Array(
  2693. { parent: scaleChildrenTable, text: Language.$('optionPanel_section_zoom_label_scaleChildren_description') },
  2694. { parent: accessKeysTable, text: Language.$('optionPanel_section_zoom_label_accessKeys') }
  2695. );
  2696.  
  2697. // Add the descriptions.
  2698. for(var i = 0; i < descriptionRowData.length; i++) {
  2699. // Add the table row.
  2700. var descriptionTr = this.addOptionsTableRow(descriptionRowData[i]['parent'], true);
  2701.  
  2702. // Add the description.
  2703. var description = myGM.addElement('p', descriptionTr.firstChild);
  2704. description.innerHTML = descriptionRowData[i]['text'];
  2705. }
  2706.  
  2707. // Set the data for the factor select fields.
  2708. var factorSelects = new Array(
  2709. { id: 'zoomWorld', selected: myGM.getValue('zoom_worldFactor', 100), labelText: Language.$('optionPanel_section_zoom_label_zoomFactor_world') },
  2710. { id: 'zoomIsland', selected: myGM.getValue('zoom_islandFactor', 100), labelText: Language.$('optionPanel_section_zoom_label_zoomFactor_island') },
  2711. { id: 'zoomTown', selected: myGM.getValue('zoom_townFactor', 100), labelText: Language.$('optionPanel_section_zoom_label_zoomFactor_town') }
  2712. );
  2713.  
  2714. // Set the zoom factor values and names.
  2715. var opts = new Array();
  2716. for(var i = ZoomFunction.minZoom; i <= ZoomFunction.maxZoom; i = i + ZoomFunction.zoomStep) {
  2717. opts.push({ value: i, name: i + '%' });
  2718. }
  2719.  
  2720. // Add all factor select fields.
  2721. for(var i = 0; i < factorSelects.length; i++) {
  2722. this.addSelect(factorTable, factorSelects[i]['id'], factorSelects[i]['selected'], opts, factorSelects[i]['labelText']);
  2723. }
  2724.  
  2725. // Set the scale children checkbox data.
  2726. var scaleChildrenCbData = new Array(
  2727. { id: 'zoomScaleChildrenWorld', checked: myGM.getValue('zoom_worldScaleChildren', true), label: Language.$('optionPanel_section_zoom_label_scaleChildren_world') },
  2728. { id: 'zoomScaleChildrenIsland', checked: myGM.getValue('zoom_islandScaleChildren', true), label: Language.$('optionPanel_section_zoom_label_scaleChildren_island') },
  2729. { id: 'zoomScaleChildrenTown', checked: myGM.getValue('zoom_townScaleChildren', true), label: Language.$('optionPanel_section_zoom_label_scaleChildren_town') }
  2730. );
  2731.  
  2732. // Add the scale children checkboxes
  2733. this.addCheckboxes(scaleChildrenTable, scaleChildrenCbData);
  2734.  
  2735. // Set the scale children checkbox data.
  2736. var accessKeysCbData = new Array(
  2737. { id: 'zoomCtrlPressed', checked: myGM.getValue('zoom_ctrlPressed', true), label: Language.$('general_ctrl') },
  2738. { id: 'zoomAltPressed', checked: myGM.getValue('zoom_altPressed', false), label: Language.$('general_alt') },
  2739. { id: 'zoomShiftPressed', checked: myGM.getValue('zoom_shiftPressed', false), label: Language.$('general_shift') }
  2740. );
  2741.  
  2742. // Add the scale children checkboxes
  2743. this.addCheckboxes(accessKeysTable, accessKeysCbData);
  2744.  
  2745. // Add the button to save the settings.
  2746. this.addSaveButton(contentWrapper);
  2747. },
  2748.  
  2749. /**
  2750. * Create the content of the message signature part.
  2751. *
  2752. * @param element contentWrapper
  2753. * The wrapper where the content should be added.
  2754. */
  2755. createMessageSigContent: function(contentWrapper) {
  2756. // Create the options tables.
  2757. var settingsTable = this.addOptionsTable(contentWrapper);
  2758. var globalSigTable = this.addOptionsTable(contentWrapper);
  2759. var serverSigTable = this.addOptionsTable(contentWrapper);
  2760.  
  2761. // Get the checkbox data.
  2762. var cbData = new Array(
  2763. { id: 'useServerSignature', checked: myGM.getValue('messageSignature_useServerSignature_' + General.getServerCode(), false), label: Language.$('optionPanel_section_messageSignature_label_useServerSignature') },
  2764. { id: 'placementTop', checked: myGM.getValue('messageSignature_placementTop', true), label: Language.$('optionPanel_section_messageSignature_label_placementTop') }
  2765. );
  2766.  
  2767. // Create the checkboxes.
  2768. this.addCheckboxes(settingsTable, cbData);
  2769.  
  2770. // Set the text area data.
  2771. var textAreaData = new Array(
  2772. { id: 'globalSignatureText', parent: globalSigTable, text: Language.$('optionPanel_section_messageSignature_label_signatureText_global'), value: myGM.getValue('messageSignature_globalSignature', '') },
  2773. { id: 'serverSignatureText', parent: serverSigTable, text: Language.$('optionPanel_section_messageSignature_label_signatureText_server'), value: myGM.getValue('messageSignature_serverSignature_' + General.getServerCode(), '') }
  2774. );
  2775.  
  2776. // Add the text areas.
  2777. for(var i = 0; i < textAreaData.length; i++) {
  2778. // Add the label table row.
  2779. var labelTr = this.addOptionsTableRow(textAreaData[i]['parent'], true);
  2780.  
  2781. // Add the label.
  2782. var label = myGM.addElement('p', labelTr.firstChild);
  2783. label.innerHTML = textAreaData[i]['text'];
  2784.  
  2785. // Add the text field table row.
  2786. var textAreaTr = this.addOptionsTableRow(textAreaData[i]['parent'], true);
  2787.  
  2788. // Add the text area.
  2789. var textArea = myGM.addElement('textarea', textAreaTr.firstChild, textAreaData[i]['id'], new Array('textfield', myGM.prefix + 'SignatureInput'));
  2790. textArea.value = textAreaData[i]['value'];
  2791. }
  2792.  
  2793. // Add the button to save the settings.
  2794. this.addSaveButton(contentWrapper);
  2795. },
  2796.  
  2797. /**
  2798. * Add a new options table.
  2799. *
  2800. * @param element wrapper
  2801. * The wrapper where the table should be added.
  2802. *
  2803. * @return element
  2804. * The body of the new table.
  2805. */
  2806. addOptionsTable: function(wrapper) {
  2807. // Create table and tablebody.
  2808. var table = myGM.addElement('table', wrapper, null, new Array('moduleContent', 'table01'));
  2809. var tableBody = myGM.addElement('tbody', table);
  2810.  
  2811. // Return the table body.
  2812. return tableBody;
  2813. },
  2814.  
  2815. /**
  2816. * Adds a new table row to a options table.
  2817. *
  2818. * @param element optionsTableBody
  2819. * The table body where the table should be added.
  2820. * @param boolean oneCell
  2821. * Decides if there is one cell or there are two cells.
  2822. *
  2823. * @return element
  2824. * The new table row.
  2825. */
  2826. addOptionsTableRow: function(optionsTableBody, oneCell) {
  2827. // Create the new table row.
  2828. var tr = myGM.addElement('tr', optionsTableBody);
  2829.  
  2830. // Create first cell.
  2831. var td1 = myGM.addElement('td', tr);
  2832.  
  2833. // If just ond cell.
  2834. if(oneCell) {
  2835. // Set width of cell to width of two cells.
  2836. td1.colSpan = 2;
  2837. td1.classList.add('left');
  2838.  
  2839. // Otherwise.
  2840. } else {
  2841. // Create second cell.
  2842. myGM.addElement('td', tr, null, 'left');
  2843. }
  2844.  
  2845. // Return the table row.
  2846. return tr;
  2847. },
  2848.  
  2849. /**
  2850. * Creates new checkboxes and adds it to a option table, specified as parentTable.
  2851. *
  2852. * @param element parentTable
  2853. * The parent of the new checkboxes.
  2854. * @param mixed[] cbData
  2855. * A array containing the data of the checkboxes.
  2856. */
  2857. addCheckboxes: function(parentTable, cbData) {
  2858. // Create the checkboxes.
  2859. for(var i = 0; i < cbData.length; i++) {
  2860. // Create table row.
  2861. var tr = this.addOptionsTableRow(parentTable, true);
  2862.  
  2863. // Create the wrapper for the checkbox and the label.
  2864. var wrapper = myGM.addElement('div', tr.firstChild, null, 'cbWrapper');
  2865.  
  2866. // Create the checkbox and set the attributes.
  2867. var cb = myGM.addElement('input', wrapper, cbData[i]['id'] + 'Cb', 'checkbox');
  2868. cb.type = 'checkbox';
  2869. cb.title = cbData[i]['label'];
  2870. cb.checked = cbData[i]['checked'] ? 'checked' : '';
  2871.  
  2872. if(cbData[i]['hrAfter'] == true) {
  2873. var spacer = this.addOptionsTableRow(parentTable, true);
  2874. myGM.addElement('hr', spacer.firstChild);
  2875. }
  2876. }
  2877.  
  2878. // Replace the checkboxes for better appearance.
  2879. ika.controller.replaceCheckboxes();
  2880. },
  2881.  
  2882. /**
  2883. * Creates a new select field and adds it to a option table, specified as parentTable.
  2884. *
  2885. * @param element parentTable
  2886. * The parent table of the new select field.
  2887. * @param String id
  2888. * The last part of the id of the select field.
  2889. * @param boolean selected
  2890. * The value of the selected option.
  2891. * @param mixed[] opts
  2892. * An array with the names an values of the options.
  2893. * @param String labelText
  2894. * The text of the select label.
  2895. */
  2896. addSelect: function(parentTable, id, selected, opts, labelText) {
  2897. // Create table row.
  2898. var tr = this.addOptionsTableRow(parentTable, false);
  2899.  
  2900. // Create label.
  2901. var selectLabel = myGM.addElement('span', tr.firstChild);
  2902. selectLabel.innerHTML = labelText;
  2903.  
  2904. // Create the wrapper for the select.
  2905. var wrapper = myGM.addElement('div', tr.lastChild, id + 'SelectContainer', new Array('select_container', 'size175'), new Array(['position', 'relative']));
  2906.  
  2907. // Create the select field.
  2908. var select = myGM.addElement('select', wrapper, id + 'Select', 'dropdown');
  2909.  
  2910. // Add the Options.
  2911. for(var i = 0; i < opts.length; i++) {
  2912. // Create an option.
  2913. var option = myGM.addElement('option', select);
  2914.  
  2915. // Set the value and the name.
  2916. option.value = opts[i]['value'];
  2917. option.innerHTML = opts[i]['name'];
  2918.  
  2919. // If the option is selected, set selected to true.
  2920. if(option.value == selected) {
  2921. option.selected = 'selected';
  2922. }
  2923. }
  2924.  
  2925. // Replace the dropdown for better appearance.
  2926. ika.controller.replaceDropdownMenus();
  2927. },
  2928.  
  2929. /**
  2930. * Creates a commit Button and adds it to a parent.
  2931. *
  2932. * @param element parent
  2933. * The parent element.
  2934. *
  2935. * @return element
  2936. * The save button.
  2937. */
  2938. addSaveButton: function(parent) {
  2939. // Create the button wrapper.
  2940. var buttonWrapper = myGM.addElement('div', parent, null, 'centerButton');
  2941.  
  2942. // Create the button.
  2943. var saveButton = myGM.addElement('input', buttonWrapper, null, 'button');
  2944. saveButton.type = 'submit';
  2945. saveButton.value = Language.$('optionPanel_save');
  2946.  
  2947. // Add a click action to the button.
  2948. saveButton.addEventListener('click', EventHandling.optionPanel.saveSettings, false);
  2949.  
  2950. return saveButton;
  2951. },
  2952.  
  2953. /**
  2954. * Save the settings.
  2955. */
  2956. saveSettings: function() {
  2957. // Save the module settings.
  2958. myGM.setValue('module_updateActive', myGM.$('#' + myGM.prefix + 'updateCb').checked);
  2959. myGM.setValue('module_incomeActive', myGM.$('#' + myGM.prefix + 'incomeOnTopCb').checked);
  2960. myGM.setValue('module_urtShortActive', myGM.$('#' + myGM.prefix + 'upkeepReductionCb').checked);
  2961. myGM.setValue('module_missingResActive', myGM.$('#' + myGM.prefix + 'missingResourcesCb').checked);
  2962. myGM.setValue('module_resourceInfoActive', myGM.$('#' + myGM.prefix + 'resourceInformationCb').checked);
  2963. myGM.setValue('module_capacityInfoActive', myGM.$('#' + myGM.prefix + 'capacityInformationCb').checked);
  2964. myGM.setValue('module_easyAccessActive', myGM.$('#' + myGM.prefix + 'easyAccessCb').checked);
  2965. myGM.setValue('module_zoomActive', myGM.$('#' + myGM.prefix + 'zoomCb').checked);
  2966. myGM.setValue('module_messageSigActive', myGM.$('#' + myGM.prefix + 'messageSignatureCb').checked);
  2967. myGM.setValue('module_easyCircularMsgActive', myGM.$('#' + myGM.prefix + 'easyCircularMessageCb').checked);
  2968. myGM.setValue('module_replaceUrlsActive', myGM.$('#' + myGM.prefix + 'replaceUrlsCb').checked);
  2969. myGM.setValue('module_colonizingLinksActive', myGM.$('#' + myGM.prefix + 'colonizingLinksCb').checked);
  2970. myGM.setValue('module_lcMoveActive', myGM.$('#' + myGM.prefix + 'loadingCircleMoveCb').checked);
  2971. myGM.setValue('module_ttAutoActive', myGM.$('#' + myGM.prefix + 'tooltipsAutoCb').checked);
  2972. myGM.setValue('module_directMilitaryTtActive', myGM.$('#' + myGM.prefix + 'directMilitaryTooltipCb').checked);
  2973. myGM.setValue('module_unitInfoActive', myGM.$('#' + myGM.prefix + 'unitInfoCb').checked);
  2974. myGM.setValue('module_hideBirdsActive', myGM.$('#' + myGM.prefix + 'hideBirdsCb').checked);
  2975. myGM.setValue('module_nctAdvisorActive', myGM.$('#' + myGM.prefix + 'noCenterTownAdvisorCb').checked);
  2976. myGM.setValue('module_memberInfoActive', myGM.$('#' + myGM.prefix + 'memberInformationCb').checked);
  2977.  
  2978. // Save the update settings.
  2979. myGM.setValue('updater_updateInterval', General.getInt(General.getSelectValue('updateIntervalSelect')));
  2980.  
  2981. // Save the resource information / missing resources settings.
  2982. myGM.setValue('resourceInfo_hourlyIncomeStyle', General.getSelectValue('hourlyIncomeStyleSelect'));
  2983. myGM.setValue('resourceInfo_capacityStyle_orientation', General.getSelectValue('capacityInfoStyleSelect'));
  2984. myGM.setValue('resourceInfo_capacityStyle_hasBorder', myGM.$('#' + myGM.prefix + 'hasBorderCb').checked);
  2985. myGM.setValue('resourceInfo_showBranchRes', myGM.$('#' + myGM.prefix + 'showBranchResCb').checked);
  2986. myGM.setValue('missingRes_showPositive', myGM.$('#' + myGM.prefix + 'showPositiveCb').checked);
  2987. myGM.setValue('missingRes_showColoured', myGM.$('#' + myGM.prefix + 'showColouredCb').checked);
  2988.  
  2989. // Save the zoom function settings.
  2990. myGM.setValue('zoom_worldFactor', General.getInt(General.getSelectValue('zoomWorldSelect')));
  2991. myGM.setValue('zoom_islandFactor', General.getInt(General.getSelectValue('zoomIslandSelect')));
  2992. myGM.setValue('zoom_townFactor', General.getInt(General.getSelectValue('zoomTownSelect')));
  2993. myGM.setValue('zoom_worldScaleChildren', myGM.$('#' + myGM.prefix + 'zoomScaleChildrenWorldCb').checked);
  2994. myGM.setValue('zoom_islandScaleChildren', myGM.$('#' + myGM.prefix + 'zoomScaleChildrenIslandCb').checked);
  2995. myGM.setValue('zoom_townScaleChildren', myGM.$('#' + myGM.prefix + 'zoomScaleChildrenTownCb').checked);
  2996. myGM.setValue('zoom_ctrlPressed', myGM.$('#' + myGM.prefix + 'zoomCtrlPressedCb').checked);
  2997. myGM.setValue('zoom_altPressed', myGM.$('#' + myGM.prefix + 'zoomAltPressedCb').checked);
  2998. myGM.setValue('zoom_shiftPressed', myGM.$('#' + myGM.prefix + 'zoomShiftPressedCb').checked);
  2999.  
  3000. // Save the message signature function settings.
  3001. myGM.setValue('messageSignature_useServerSignature_' + General.getServerCode(), myGM.$('#' + myGM.prefix + 'useServerSignatureCb').checked);
  3002. myGM.setValue('messageSignature_placementTop', myGM.$('#' + myGM.prefix + 'placementTopCb').checked);
  3003. myGM.setValue('messageSignature_globalSignature', myGM.$('#' + myGM.prefix + 'globalSignatureText').value);
  3004. myGM.setValue('messageSignature_serverSignature_' + General.getServerCode(), myGM.$('#' + myGM.prefix + 'serverSignatureText').value);
  3005.  
  3006. // Show success hint.
  3007. General.showTooltip('cityAdvisor', 'confirm', Language.$('general_successful'));
  3008. }
  3009. };
  3010.  
  3011. /**
  3012. * Functions for zooming world, island and town view.
  3013. */
  3014. ZoomFunction = {
  3015. /**
  3016. * The minimal zoom factor in percent.
  3017. */
  3018. minZoom: 55,
  3019.  
  3020. /**
  3021. * The maximal zoom factor in percent.
  3022. */
  3023. maxZoom: 150,
  3024.  
  3025. /**
  3026. * The step between two zoom factors in percent.
  3027. */
  3028. zoomStep: 5,
  3029.  
  3030. /**
  3031. * Init the zooming.
  3032. */
  3033. init: function() {
  3034. // Get the min zoom.
  3035. var minZoom = Math.round(ika.worldview_scale_min * 100);
  3036. minZoom = minZoom + ((minZoom % 5 == 0) ? 0 : (5 - minZoom % 5));
  3037.  
  3038. // Set the max and min zoom.
  3039. this.minZoom = minZoom;
  3040. ika.worldview_scale_min = minZoom / 100;
  3041. ika.worldview_scale_max = this.maxZoom / 100;
  3042.  
  3043. // Change the mousewheel listener, so that it is possible to cheack if there are also keys pressed.
  3044. this.changeMouseWheelListener();
  3045.  
  3046. // Get the zooming factor.
  3047. var factor = myGM.getValue('zoom_' + View.name + 'Factor', 100);
  3048.  
  3049. // Add the zoom Buttons.
  3050. this.addZoomButtons();
  3051.  
  3052. // Zoom.
  3053. this.zoom(factor);
  3054. },
  3055.  
  3056. /**
  3057. * Add the Buttons for zooming to the view.
  3058. */
  3059. addZoomButtons: function() {
  3060. // Get the the script toolbar
  3061. var scriptToolbar = myGM.$('#' + myGM.prefix + 'toolbar');
  3062.  
  3063. // Create the zoom buttons.
  3064. var zoomWrapper = myGM.addElement('div', scriptToolbar, 'zoomWrapper');
  3065. var zoomIn = myGM.addElement('div', zoomWrapper, 'zoomIn', 'maximizeImg');
  3066. var zoomFactor = myGM.addElement('div', zoomWrapper, 'zoomFactor');
  3067. var zoomOut = myGM.addElement('div', zoomWrapper, 'zoomOut', 'minimizeImg');
  3068. // Set the button title.
  3069. zoomIn.title = Language.$('zoomFunction_zoomIn');
  3070. zoomFactor.title = Language.$('zoomFunction_zoomFactor');
  3071. zoomOut.title = Language.$('zoomFunction_zoomOut');
  3072. // Add the event listener.
  3073. zoomIn.addEventListener('click', EventHandling.zoomFunction.zoomIn, false);
  3074. zoomOut.addEventListener('click', EventHandling.zoomFunction.zoomOut, false);
  3075.  
  3076. // Add the styles.
  3077. myGM.addStyle(
  3078. "#" + myGM.prefix + "zoomWrapper { position: absolute; top: 2px; right: 0px; width: 72px; transform: scale(0.75); scale(0.75); -o-transform: scale(0.75); -webkit-transform: scale(0.75); } \
  3079. #" + myGM.prefix + "zoomIn { position: absolute; } \
  3080. #" + myGM.prefix + "zoomFactor { position: absolute; left: 20px; width: 35px; text-align: center; } \
  3081. #" + myGM.prefix + "zoomOut { position: absolute; left: 58px; }"
  3082. );
  3083. },
  3084.  
  3085. /**
  3086. * Changes the mouse wheel listener so that it could be used with access keys.
  3087. */
  3088. changeMouseWheelListener: function() {
  3089. // Set the defaut scrollDiv id.
  3090. var scrollDivId = '#worldmap';
  3091. // If wolrd view, change the scroll div id.
  3092. if(View.name == 'world') {
  3093. scrollDivId = '#map1';
  3094. }
  3095.  
  3096. // Remove the old mouse wheel listener. (with the use of Ikariam-jQuery)
  3097. win.$(scrollDivId).unbind('mousewheel');
  3098.  
  3099. // Add the new mouse wheel listener.
  3100. var scrollDiv = myGM.$(scrollDivId);
  3101. scrollDiv.addEventListener('DOMMouseScroll', EventHandling.zoomFunction.mouseScroll, false);
  3102. scrollDiv.addEventListener('mousewheel', EventHandling.zoomFunction.mouseScroll, false);
  3103. },
  3104.  
  3105. /**
  3106. * Zooms the view.
  3107. *
  3108. * @param int factor
  3109. * The factor which is used.
  3110. */
  3111. zoom: function(factor) {
  3112. // If the factor is bigger / smaller than allowed, set it to the max / min allowed.
  3113. factor = factor > this.maxZoom ? this.maxZoom : (factor < this.minZoom ? this.minZoom : factor);
  3114.  
  3115. // Store the zoom factor.
  3116. myGM.setValue('zoom_' + View.name + 'Factor', factor);
  3117.  
  3118. // Update the zoom factor which is shown to the user and the zoom buttons.
  3119. var zoomFactorDiv = myGM.$('#' + myGM.prefix + 'zoomFactor');
  3120. var zoomIn = myGM.$('#' + myGM.prefix + 'zoomIn');
  3121. var zoomOut = myGM.$('#' + myGM.prefix + 'zoomOut');
  3122.  
  3123. // Zoom factor.
  3124. if(zoomFactorDiv) {
  3125. zoomFactorDiv.innerHTML = factor + '%';
  3126. }
  3127. // Zoom in.
  3128. if(zoomIn) {
  3129. // If it is not allowed to zoom in, hide the zoom in button.
  3130. if(factor >= this.maxZoom) {
  3131. zoomIn.classList.add('invisible');
  3132.  
  3133. // Otherwise: Show the zoom in button.
  3134. } else {
  3135. zoomIn.classList.remove('invisible');
  3136. }
  3137. }
  3138.  
  3139. // Zoom out.
  3140. if(zoomOut) {
  3141. // If it is not allowed to zoom out, hide the zoom out button.
  3142. if(factor <= this.minZoom) {
  3143. zoomOut.classList.add('invisible');
  3144.  
  3145. // Otherwise: Show the zoom out button.
  3146. } else {
  3147. zoomOut.classList.remove('invisible');
  3148. }
  3149. }
  3150.  
  3151. // Get the factor as normal number, not as percentage.
  3152. var factorNew = factor / 100.0;
  3153.  
  3154. // Get the game scaling factor depending on the view.
  3155. var scale = 0;
  3156. switch(View.name) {
  3157. case 'world':
  3158. break;
  3159.  
  3160. case 'island':
  3161. scale = ika.worldview_scale_island;
  3162. break;
  3163.  
  3164. case 'town':
  3165. scale = ika.worldview_scale_city;
  3166. break;
  3167.  
  3168. default:
  3169. return;
  3170. break;
  3171. }
  3172.  
  3173. // It is the world view, zoom it.
  3174. if(View.name == 'world') {
  3175. this.zoomWorld(factorNew);
  3176.  
  3177. // Otherwise call the ikariam zoom function.
  3178. } else {
  3179. // Get the number of steps to zoom.
  3180. var stepNumber = Math.round((factorNew - scale) / .05);
  3181.  
  3182. // Get the center position of the worldmap.
  3183. var worldview = myGM.$('#worldview');
  3184. var posX = worldview.offsetLeft + worldview.offsetWidth / 2;
  3185. var posY = worldview.offsetTop + worldview.offsetHeight / 2;
  3186.  
  3187. // Zoom.
  3188. ika.controller.scaleWorldMap(stepNumber, posX, posY);
  3189. }
  3190.  
  3191. // Scale child elements if enabled.
  3192. if(myGM.getValue('zoom_' + View.name + 'ScaleChildren', true)) ZoomFunction.scaleChildren(factorNew);
  3193.  
  3194. // Return the factor in percent.
  3195. return factor;
  3196. },
  3197.  
  3198. /**
  3199. * Zoom the world view.
  3200. *
  3201. * @param float factor
  3202. * The factor which is used.
  3203. */
  3204. zoomWorld: function(factor) {
  3205. // Get the factor the scrollcover must be moved.
  3206. var translateXY = (100 - 100 / factor) / 2;
  3207.  
  3208. // Get the new height and width of the scrollcover.
  3209. var heightWidth = 100 / factor;
  3210.  
  3211. // Overwrite the old style.
  3212. myGM.addStyle(
  3213. "#scrollcover { transform: scale(" + factor + ") translate(" + translateXY + "%, " + translateXY + "%); -o-transform: scale(" + factor + ") translate(" + translateXY + "%, " + translateXY + "%); -webkit-transform: scale(" + factor + ") translate(" + translateXY + "%, " + translateXY + "%); height: " + heightWidth + "% !important; width: " + heightWidth + "% !important; }",
  3214. 'zoomWorld', true
  3215. );
  3216.  
  3217. // Get the map.
  3218. var map = myGM.$('#map1');
  3219. // Set the new offset.
  3220. var newWmTop = 0;
  3221. var newWmLeft = 0;
  3222. // Apply the new offset to the map.
  3223. map.style.top = newWmTop + 'px';
  3224. map.style.left = newWmLeft + 'px';
  3225. },
  3226.  
  3227. /**
  3228. * Scales the children of the worldmap / island view.
  3229. *
  3230. * @param float factor
  3231. * The factor which is used.
  3232. * @param String view
  3233. * The name of the view.
  3234. */
  3235. scaleChildren: function(factor) {
  3236. // Which view is used?
  3237. switch(View.name) {
  3238. // Worldview.
  3239. case 'world':
  3240. myGM.addStyle(
  3241. ".islandTile .wonder, .islandTile .tradegood, .islandTile .cities" + (factor < 1 ? ", .ownerState" : "") + " { transform: scale(" + 1 / factor + "); -o-transform: scale(" + 1 / factor + "); -webkit-transform: scale(" + 1 / factor + "); } \
  3242. .islandTile .cities { bottom: 10px !important; }",
  3243. 'scaleChildren', true
  3244. );
  3245. break;
  3246.  
  3247. // Island view.
  3248. case 'island':
  3249. myGM.addStyle(
  3250. ".cityLocation .scroll_img { transform: scale(" + 1 / factor + "); -o-transform: scale(" + 1 / factor + "); -webkit-transform: scale(" + 1 / factor + "); }",
  3251. 'scaleChildren', true
  3252. );
  3253. break;
  3254.  
  3255. // Town view.
  3256. case 'town':
  3257. myGM.addStyle(
  3258. ".building .timetofinish { transform: scale(" + 1 / factor + "); -o-transform: scale(" + 1 / factor + "); -webkit-transform: scale(" + 1 / factor + "); }",
  3259. 'scaleChildren', true
  3260. );
  3261. break;
  3262.  
  3263. // Default: do nothing.
  3264. default:
  3265. return;
  3266. break;
  3267. }
  3268. }
  3269. };
  3270.  
  3271. /**
  3272. * Function for showing the missing resources directly in the ruction / update view.
  3273. */
  3274. MissingResources = {
  3275. /**
  3276. * Init the missing ressources.
  3277. */
  3278. init: function() {
  3279. // Set the required styles.
  3280. myGM.addStyle(
  3281. "#sidebar #buildingUpgrade ul.resources li { width: 120px; } \
  3282. #sidebar #buildingUpgrade ul.resources li.time { width: 60px !important; } \
  3283. #sidebar ." + myGM.prefix + "missingResources { float: right; }"
  3284. );
  3285. },
  3286.  
  3287. /**
  3288. * Shows the missing resources in the building ground popup.
  3289. */
  3290. showInBuildingGround: function() {
  3291. // Show the resources.
  3292. this.show(false);
  3293. },
  3294.  
  3295. /**
  3296. * Shows the missing resources in the sidebar.
  3297. */
  3298. showInSidebar: function() {
  3299. // Show the resources.
  3300. this.show(true);
  3301.  
  3302. // Add an event listener to update the shown resources.
  3303. myGM.$('#cityResources').addEventListener('DOMSubtreeModified', EventHandling.missingResources.resourcesUpdated, false);
  3304. },
  3305.  
  3306. /**
  3307. * Shows the missing resources in the place which is given.
  3308. *
  3309. * @param boolean isSidebar
  3310. * If the place where to show is the sidebar.
  3311. */
  3312. show: function(isSidebar, update) {
  3313. // Store the current resources.
  3314. var current = Array();
  3315. current.push(ika.model.currentResources.resource); // Wood.
  3316. current.push(ika.model.currentResources[1]); // Wine.
  3317. current.push(ika.model.currentResources[2]); // Marble.
  3318. current.push(ika.model.currentResources[3]); // Crystal.
  3319. current.push(ika.model.currentResources[4]); // Sulfur.
  3320.  
  3321. // Store the resource names.
  3322. var resourceName = new Array('wood', 'wine', 'marble', 'glass', 'sulfur');
  3323.  
  3324. // Get all possible wrappers.
  3325. var wrapper = myGM.$$('#' + (isSidebar ? 'sidebar' : 'buildingGround') + ' .resources');
  3326.  
  3327. // Loop over all wrappers.
  3328. for(var i = 0; i < wrapper.length; i++) {
  3329. // Loop over all resources.
  3330. for(var k = 0; k < resourceName.length; k++) {
  3331. // Get the resource node.
  3332. var node = myGM.$('.' + resourceName[k], wrapper[i]);
  3333.  
  3334. // If the node exist, show the missing resources.
  3335. if(node) {
  3336. // Get the missing resource number.
  3337. var missing = current[k] - (update ? General.getInt(node.lastChild.previousSibling.nodeValue) : General.getInt(node.lastChild.nodeValue));
  3338.  
  3339. // If there are resources missing.
  3340. if(missing < 0 || (myGM.getValue('missingRes_showPositive', true) && isSidebar)) {
  3341. // Show the missing resource info.
  3342. var show = update ? myGM.$('#' + myGM.prefix + 'missingResources' + resourceName[k]) : myGM.addElement('span', node, 'missingResources' + resourceName[k], 'missingResources', null, true);
  3343. show.innerHTML = (isSidebar ? '' : ' (') + General.formatToIkaNumber(missing, myGM.getValue('missingRes_showColoured', true), true) + (isSidebar ? '' : ')');
  3344. }
  3345. }
  3346. }
  3347. }
  3348. }
  3349. };
  3350.  
  3351. /**
  3352. * Functions for showing the hourly production directly in the town view.
  3353. * Also adds the daily Production to the resource tooltip.
  3354. */
  3355. ResourceInfo = {
  3356. /**
  3357. * Storage for the last tradegood.
  3358. */
  3359. lastTradegood: null,
  3360.  
  3361. /**
  3362. * Inits the resource info.
  3363. */
  3364. init: function() {
  3365. // If the city is owned by the player.
  3366. if(ika.model.isOwnCity == true) {
  3367. // Set the styles.
  3368. this.setStyles();
  3369. // Add the resource info.
  3370. if(myGM.getValue('module_resourceInfoActive', true)) this.addHourlyResourceInfo();
  3371. // Add the capacity info.
  3372. if(myGM.getValue('module_capacityInfoActive', true)) this.addCapacityInfo();
  3373. }
  3374. },
  3375.  
  3376. /**
  3377. * Sets the styles that are required for the resource info.
  3378. */
  3379. setStyles: function() {
  3380. // Probleme: resourceInfo deaktiviert + alle.
  3381. var resourceInfoActive = myGM.getValue('module_resourceInfoActive', true);
  3382. var capacityInfoActive = myGM.getValue('module_capacityInfoActive', true);
  3383. var hourlyIncomeStyle = myGM.getValue('resourceInfo_hourlyIncomeStyle', 'alignRight');
  3384. var capacityStyleOrientation = myGM.getValue('resourceInfo_capacityStyle_orientation', 'vertical');
  3385. // If the resource info is active.
  3386. if(resourceInfoActive) {
  3387. // Define the style for the resources wrapper.
  3388. var resourcesStyle = {
  3389. lineHeight: (capacityInfoActive && capacityStyleOrientation == 'horizontal') ? 'line-height: 9px !important;' : 'line-height: 11px !important;',
  3390. align: (hourlyIncomeStyle != 'alignLeft') ? ' text-align: right;' : '',
  3391. fontSize: (capacityInfoActive && capacityStyleOrientation != 'horizontalFull') ? ' font-size: 11px;' : '',
  3392. height: (capacityInfoActive && capacityStyleOrientation == 'horizontalFull') ? ' height: 32px !important;' : '',
  3393. top: (capacityInfoActive && capacityStyleOrientation == 'horizontalFull') ? ' top: -2px !important;' : '',
  3394. paddingLeft: (capacityInfoActive && capacityStyleOrientation == 'vertical' && hourlyIncomeStyle == 'alignLeft') ? ' padding-left: 38px !important;' : ''
  3395. };
  3396. var resStyle = '#resources_wood, #resources_wine, #resources_marble, #resources_glass, #resources_sulfur { '
  3397. + resourcesStyle.lineHeight
  3398. + resourcesStyle.align
  3399. + resourcesStyle.fontSize
  3400. + resourcesStyle.height
  3401. + resourcesStyle.top
  3402. + resourcesStyle.paddingLeft
  3403. + ' } ';
  3404. // Define the style for the global menu resources.
  3405. var globalMenuStyle = {
  3406. padding: (hourlyIncomeStyle != 'alignLeft') ? 'padding-right: 4px;' : ''
  3407. };
  3408. var globMenuStyle = '#js_GlobalMenu_wood, #js_GlobalMenu_wine, #js_GlobalMenu_marble, #js_GlobalMenu_crystal, #js_GlobalMenu_sulfur { '
  3409. + globalMenuStyle.padding
  3410. + ' } ';
  3411. // Define the style for the hourly income.
  3412. var incomeStyle = {
  3413. fontSize: (capacityInfoActive && capacityStyleOrientation != 'horizontalFull') ? 'font-size: 9px;' : 'font-size: 11px;',
  3414. paddingRight: (hourlyIncomeStyle != 'alignLeft') ? ' padding-right: 4px;' : ''
  3415. };
  3416. var hirStyle = '.' + myGM.prefix + 'hourlyIncomeResource { '
  3417. + incomeStyle.fontSize
  3418. + incomeStyle.paddingRight
  3419. + ' } ';
  3420. // Define the style for the resources wrapper with border as separation.
  3421. var separationStyle = {
  3422. border: (hourlyIncomeStyle == 'withSeparation') ? 'border-right: 1px dotted #542C0F;' : ''
  3423. };
  3424. var sepStyle = '#resources_wood, #resources_wine, #resources_marble, #resources_glass { '
  3425. + separationStyle.border
  3426. + ' } ';
  3427. // Add all styles to the ikariam page.
  3428. var resInfoStyle = resStyle + globMenuStyle + hirStyle + sepStyle;
  3429. myGM.addStyle(resInfoStyle, 'hourlyIncomeStyle', true);
  3430. // Otherwise: If the capacity info is active.
  3431. } else if(capacityInfoActive) {
  3432. // Define the style for the resources wrapper.
  3433. var resourcesStyle = {
  3434. lineHeight: (capacityStyleOrientation == 'horizontal') ? 'line-height: 12px !important;' : ((capacityStyleOrientation == 'vertical') ? ' line-height: 24px !important;' : ''),
  3435. align: (hourlyIncomeStyle != 'alignLeft') ? ' text-align: right;' : '',
  3436. fontSize: (capacityStyleOrientation == 'vertical') ? ' font-size: 11px;' : '',
  3437. height: (capacityStyleOrientation == 'horizontalFull') ? ' height: 32px !important;' : '',
  3438. top: (capacityStyleOrientation == 'horizontalFull') ? ' top: -2px !important;' : '',
  3439. paddingLeft: (capacityStyleOrientation == 'vertical' && hourlyIncomeStyle == 'alignLeft') ? ' padding-left: 38px !important;' : ''
  3440. };
  3441. var resStyle = '#resources_wood, #resources_wine, #resources_marble, #resources_glass, #resources_sulfur { '
  3442. + resourcesStyle.lineHeight
  3443. + resourcesStyle.align
  3444. + resourcesStyle.fontSize
  3445. + resourcesStyle.height
  3446. + resourcesStyle.top
  3447. + resourcesStyle.paddingLeft
  3448. + ' } ';
  3449. // Define the style for the global menu resources.
  3450. var globalMenuStyle = {
  3451. padding: (hourlyIncomeStyle != 'alignLeft') ? 'padding-right: 4px;' : ''
  3452. };
  3453. var globMenuStyle = '#js_GlobalMenu_wood, #js_GlobalMenu_wine, #js_GlobalMenu_marble, #js_GlobalMenu_crystal, #js_GlobalMenu_sulfur { '
  3454. + globalMenuStyle.padding
  3455. + ' } ';
  3456. // Define the style for the resources wrapper with border as separation.
  3457. var separationStyle = {
  3458. border: (hourlyIncomeStyle == 'withSeparation') ? 'border-right: 1px dotted #542C0F;' : ''
  3459. };
  3460. var sepStyle = '#resources_wood, #resources_wine, #resources_marble, #resources_glass { '
  3461. + separationStyle.border
  3462. + ' } ';
  3463. // Add all styles to the ikariam page.
  3464. var resInfoStyle = resStyle + globMenuStyle + sepStyle;
  3465. myGM.addStyle(resInfoStyle, 'hourlyIncomeStyle', true);
  3466. }
  3467. // If the capacity info is active.
  3468. if(capacityInfoActive) {
  3469. // Define the style for the bar.
  3470. var barStyle = '.' + myGM.prefix + 'bar { height: 100%; width: 100%; bottom: 0px; position: absolute; } ';
  3471. // Define the style for the red bar.
  3472. var redBarStyle = '.' + myGM.prefix + 'bar.' + myGM.prefix + 'red { background-color: #AA0000; } ';
  3473. // Define the style for the red bar.
  3474. var yellowBarStyle = '.' + myGM.prefix + 'bar.' + myGM.prefix + 'yellow { background-color: #FFD700; } ';
  3475. // Define the style for the green bar.
  3476. var greenBarStyle = '.' + myGM.prefix + 'bar.' + myGM.prefix + 'green { background-color: #669900; } ';
  3477. // Define the style for the capacity information.
  3478. var capacityInfoStyle = {
  3479. position: 'position: absolute;',
  3480. height: (capacityStyleOrientation == 'vertical') ? ' height: 21px;' : ' height: 4px;',
  3481. width: (capacityStyleOrientation != 'vertical') ? ((capacityStyleOrientation == 'horizontal') ? ' width: 50px;' : ' width: 79px;') : ' width: 4px;',
  3482. bottom: ' bottom: 4px;',
  3483. right: (capacityStyleOrientation != 'vertical') ? ' right: 4px;' : '',
  3484. marginLeft: (capacityStyleOrientation == 'vertical' && hourlyIncomeStyle == 'alignLeft') ? ' margin-left: -7px;' : ''
  3485. };
  3486. var capInfoStyle = '.' + myGM.prefix + 'capacityInfo { '
  3487. + capacityInfoStyle.position
  3488. + capacityInfoStyle.height
  3489. + capacityInfoStyle.width
  3490. + capacityInfoStyle.bottom
  3491. + capacityInfoStyle.right
  3492. + capacityInfoStyle.marginLeft
  3493. + ' } ';
  3494. // Define the style for the capacity information with border.
  3495. var capacityInfoBorderStyle = {
  3496. border: 'border: 1px inset #906646;',
  3497. height: (capacityStyleOrientation == 'vertical') ? ' height: 20px;' : ' height: 3px;',
  3498. width: (capacityStyleOrientation != 'vertical') ? ((capacityStyleOrientation == 'horizontal') ? ' width: 50px;' : ' width: 78px;') : ' width: 3px;',
  3499. bottom: ' bottom: 3px;',
  3500. right: (capacityStyleOrientation != 'vertical') ? ' right: 3px;' : ''
  3501. };
  3502. var capInfoBorderStyle = '.' + myGM.prefix + 'capacityInfo.' + myGM.prefix + 'border { '
  3503. + capacityInfoBorderStyle.border
  3504. + capacityInfoBorderStyle.height
  3505. + capacityInfoBorderStyle.width
  3506. + capacityInfoBorderStyle.bottom
  3507. + capacityInfoBorderStyle.right
  3508. + ' } ';
  3509. // Add all styles to the ikariam page.
  3510. var capacityInformationStyle = barStyle + redBarStyle + yellowBarStyle + greenBarStyle + capInfoStyle + capInfoBorderStyle;
  3511. myGM.addStyle(capacityInformationStyle, 'capacityInfoStyle', true);
  3512. }
  3513. },
  3514.  
  3515. /**
  3516. * Show the hourly resource info.
  3517. */
  3518. addHourlyResourceInfo: function() {
  3519. // If the info containers are already set, do nothing.
  3520. if(myGM.$('#' + myGM.prefix + 'wood')) {
  3521. return;
  3522. }
  3523. // Set all resource types.
  3524. var types = new Array('wood', 'wine', 'marble', 'glass', 'sulfur');
  3525.  
  3526. // Loop over all resource types.
  3527. for(var i = 0; i < types.length; i++) {
  3528. // Add the hourly info.
  3529. myGM.addElement('span', myGM.$('#resources_' + types[i]), 'hourlyIncomeResource' + types[i], 'hourlyIncomeResource', null, true);
  3530.  
  3531. // Add the daily info.
  3532. var parent = myGM.$('#resources_' + types[i] + ' .tooltip');
  3533. var classes = (i < 2) ? 'smallFont' : new Array('smallFont', 'invisible');
  3534. var wrapper = myGM.addElement('p', parent, 'dailyIncomeResourceWrapper' + types[i], classes, null, null, myGM.$('p:nth-child(2)', parent));
  3535. wrapper.innerHTML = Language.$('resourceInfo_dailyIncome_' + types[i]) + ' ';
  3536. myGM.addElement('span', wrapper, 'dailyIncomeResource' + types[i]);
  3537. }
  3538.  
  3539. // Update the resource information.
  3540. this.updateHourlyResourceInfo(true);
  3541. },
  3542.  
  3543. /**
  3544. * Update the shown hourly resource information.
  3545. */
  3546. updateHourlyResourceInfo: function(firstRun) {
  3547. // If the city is not owned by the player, stop the update.
  3548. if(ika.model.isOwnCity != true) {
  3549. return;
  3550. }
  3551. // Prefix for selection.
  3552. var hourlyPrefix = '#' + myGM.prefix + 'hourlyIncomeResource';
  3553. var dailyPrefix = '#' + myGM.prefix + 'dailyIncomeResource';
  3554. // If the info containers are not set, set them.
  3555. if(!myGM.$(hourlyPrefix + 'wood')) {
  3556. this.init();
  3557. }
  3558. // Set all resource types.
  3559. var types = new Array('wood', 'wine', 'marble', 'glass', 'sulfur');
  3560.  
  3561. // Get some needed data.
  3562. var producedTradegood = ika.model.producedTradegood;
  3563. var tradegood = ika.model.tradegoodProduction * 3600 + 0.001;
  3564. var resource = ika.model.resourceProduction * 3600 + 0.001;
  3565. var wineSpending = ika.model.wineSpendings;
  3566. var producesWine = ika.model.cityProducesWine;
  3567.  
  3568. // Bugfix for the first running of the script on the page: wineSpending is not reduced by vineyard.
  3569. if(firstRun) {
  3570. // Get the vineyard.
  3571. var vineyard = myGM.$('#locations .vineyard');
  3572.  
  3573. // If a vineyard exists, reduce the wine spending.
  3574. if(vineyard) {
  3575. // Get the vineyard level.
  3576. var level = vineyard.className.match(/level([0-9]*)/i)[1];
  3577.  
  3578. // Reduce the wine spending.
  3579. wineSpending = (wineSpending * (100 - level)) / 100;
  3580. }
  3581. }
  3582.  
  3583. // Update the wood data.
  3584. myGM.$(hourlyPrefix + types[0]).innerHTML = '<br>' + General.formatToIkaNumber(Math.floor(resource), true, true);
  3585. myGM.$(dailyPrefix + types[0]).innerHTML = General.formatToIkaNumber(Math.floor(resource * 24), false);
  3586.  
  3587. // Delete the last tradegood info.
  3588. if(this.lastTradegood) {
  3589. // Hourly.
  3590. myGM.$(hourlyPrefix + types[this.lastTradegood]).innerHTML = '';
  3591.  
  3592. // Daily.
  3593. myGM.$(dailyPrefix + types[this.lastTradegood]).innerHTML = '';
  3594.  
  3595. // Hide the daily tradegood info if it is not wine.
  3596. if(this.lastTradegood != 1) myGM.$(dailyPrefix + 'Wrapper' + types[this.lastTradegood]).classList.add('invisible');
  3597. }
  3598.  
  3599. // Add the info if the city produces wine.
  3600. if(producesWine) {
  3601. // Hourly.
  3602. myGM.$(hourlyPrefix + types[producedTradegood]).innerHTML = '<br>' + General.formatToIkaNumber(Math.floor(tradegood - wineSpending), true, true);
  3603.  
  3604. // Daily.
  3605. myGM.$(dailyPrefix + types[producedTradegood]).innerHTML = General.formatToIkaNumber(Math.floor((tradegood - wineSpending) * 24), false);
  3606.  
  3607. // Add the info if the city doesn't produces wine.
  3608. } else {
  3609. // Hourly.
  3610. myGM.$(hourlyPrefix + types[producedTradegood]).innerHTML = '<br>' + General.formatToIkaNumber(Math.floor(tradegood), true, true);
  3611. myGM.$(hourlyPrefix + types[1]).innerHTML = '<br>' + General.formatToIkaNumber(Math.floor(-1 * wineSpending), true, true);
  3612.  
  3613. // Daily.
  3614. myGM.$(dailyPrefix + types[producedTradegood]).innerHTML = General.formatToIkaNumber(Math.floor(tradegood * 24), false);
  3615. myGM.$(dailyPrefix + types[1]).innerHTML = General.formatToIkaNumber(Math.floor(-1 * wineSpending * 24), false);
  3616. }
  3617.  
  3618. // Show the daily tradegood info.
  3619. myGM.$(dailyPrefix + 'Wrapper' + types[producedTradegood]).classList.remove('invisible');
  3620.  
  3621. // Store the actual tradegood as last produced tradegood.
  3622. this.lastTradegood = producedTradegood;
  3623. },
  3624. /**
  3625. * Add a bar showing the status of the filling of the warehouse.
  3626. */
  3627. addCapacityInfo: function() {
  3628. // If the info containers are already set, do nothing.
  3629. if(myGM.$('#' + myGM.prefix + 'capacityInfowood')) {
  3630. return;
  3631. }
  3632. // Set all resource types.
  3633. var types = new Array('wood', 'wine', 'marble', 'glass', 'sulfur');
  3634. // Has the capacity information a border?
  3635. var hasBorder = myGM.getValue('resourceInfo_capacityStyle_hasBorder', true);
  3636. // Loop over all resource types.
  3637. for(var i = 0; i < types.length; i++) {
  3638. // Add the hourly info.
  3639. var wrapper = myGM.addElement('div', myGM.$('#resources_' + types[i]), 'capacityInfo' + types[i], hasBorder ? new Array('capacityInfo', 'border') : 'capacityInfo', null, true);
  3640. myGM.addElement('div', wrapper, 'maxCapacity' + types[i], new Array('bar', 'yellow'), null, true);
  3641. myGM.addElement('div', wrapper, 'warehouseCapacity' + types[i], new Array('bar', 'green'), null, true);
  3642. myGM.addElement('div', wrapper, 'currentResource' + types[i], new Array('bar', 'red'), null, true);
  3643. }
  3644. // Update the resource information.
  3645. this.updateCapacityInfo();
  3646. // Add an event listener to update the shown resources.
  3647. myGM.$('#cityResources').addEventListener('DOMSubtreeModified', ResourceInfo.updateCapacityInfo, false);
  3648. },
  3649. /**
  3650. * Update the bar showing the status of the filling of the warehouse.
  3651. */
  3652. updateCapacityInfo: function() {
  3653. // If the city is not owned by the player, stop the update.
  3654. if(ika.model.isOwnCity != true) {
  3655. return;
  3656. }
  3657. // Prefix for selection and user deifned settings.
  3658. var prefix = '#' + myGM.prefix;
  3659. var orientation = myGM.getValue('resourceInfo_capacityStyle_orientation', 'vertical');
  3660. var showBranchOfficeRes = myGM.getValue('resourceInfo_showBranchRes', true);
  3661. // If the info containers are not set, set them.
  3662. if(!myGM.$(prefix + 'capacityInfowood')) {
  3663. this.init();
  3664. }
  3665. // Set all resource types.
  3666. var types = new Array('wood', 'wine', 'marble', 'glass', 'sulfur');
  3667. // Get some needed data.
  3668. var warehouseCapacity = new Array();
  3669. var branchOfficeResources = new Array();
  3670. var currentResources = new Array();
  3671. warehouseCapacity.push(ika.model.maxResources.resource);
  3672. branchOfficeResources.push(ika.model.branchOfficeResources.resource);
  3673. currentResources.push(ika.model.currentResources.resource);
  3674. for(var i = 1; i < types.length; i++) {
  3675. warehouseCapacity.push(ika.model.maxResources[i]);
  3676. branchOfficeResources.push(ika.model.branchOfficeResources[i]);
  3677. currentResources.push(ika.model.currentResources[i]);
  3678. }
  3679. // Set the styles for all resources.
  3680. for(var i = 0; i < types.length; i++) {
  3681. var maxCapacity = myGM.$(prefix + 'maxCapacity' + types[i]);
  3682. var curWarehouse = myGM.$(prefix + 'warehouseCapacity' + types[i]);
  3683. var curResource = myGM.$(prefix + 'currentResource' + types[i]);
  3684. var curTmp = (typeof(currentResources[i]) == 'string') ? General.getInt(currentResources[i]) : currentResources[i];
  3685. var capTmp = (typeof(warehouseCapacity[i]) == 'string') ? General.getInt(warehouseCapacity[i]) : warehouseCapacity[i];
  3686. var offTmp = (typeof(branchOfficeResources[i]) == 'string') ? General.getInt(branchOfficeResources[i]) : branchOfficeResources[i];
  3687. var maxTmp = capTmp + offTmp;
  3688. var curWarehousePercentage = showBranchOfficeRes ? (capTmp / maxTmp * 100) : 100;
  3689. var curResourcePercentage = showBranchOfficeRes ? (curTmp / maxTmp * 100) : (curTmp / capTmp * 100);
  3690. if(orientation == 'vertical') {
  3691. maxCapacity.style.height = '100%';
  3692. curWarehouse.style.height = curWarehousePercentage + '%';
  3693. curResource.style.height = curResourcePercentage + '%';
  3694. } else {
  3695. maxCapacity.style.width = '100%';
  3696. curWarehouse.style.width = curWarehousePercentage + '%';
  3697. curResource.style.width = curResourcePercentage + '%';
  3698. }
  3699. }
  3700. },
  3701. /**
  3702. * Adds links to access town hall, tradegood and sawmill to the resources menu.
  3703. */
  3704. addRessourceLinks: function() {
  3705. // Set all tradegood types.
  3706. var types = new Array('wine', 'marble', 'glass', 'sulfur');
  3707. // Get the population and wood link.
  3708. var population = myGM.$('#resources_population');
  3709. var wood = myGM.$('#resources_wood');
  3710. // Add the event listener.
  3711. population.addEventListener('click', function() { win.ajaxHandlerCall('?view=city&dialog=townHall&cityId=' + ika.getModel().relatedCityData[ika.getModel().relatedCityData.selectedCity].id + '&position=0'); }, true);
  3712. wood.addEventListener('click', function() { win.ajaxHandlerCall('?view=island&dialog=resource'); }, true);
  3713. // Add the event listener to the tradegood links.
  3714. for(var i = 0; i < types.length; i++) {
  3715. var tradegood = myGM.$('#resources_' + types[i]);
  3716. tradegood.addEventListener('click', function() { win.ajaxHandlerCall('?view=island&dialog=tradegood'); }, true);
  3717. }
  3718. // Add the styles.
  3719. myGM.addStyle(
  3720. "#resources_population:hover, #resources_wood:hover, #resources_wine:hover, #resources_marble:hover, #resources_glass:hover, #resources_sulfur:hover { text-shadow: 2px 2px 2px #666; cursor: pointer; color: #333; }",
  3721. 'resourceLinks', true);
  3722. }
  3723. };
  3724.  
  3725. /**
  3726. * Functions for the member info.
  3727. */
  3728. MemberInfo = {
  3729. /**
  3730. * Storage for the highscore type.
  3731. */
  3732. type: '',
  3733.  
  3734. /**
  3735. * Storage for all actual member information.
  3736. */
  3737. data: null,
  3738.  
  3739. /**
  3740. * Init the member info.
  3741. */
  3742. init: function() {
  3743. // Add the styles.
  3744. myGM.addStyle(
  3745. "#" + myGM.prefix + "resetInfo { float: right; margin-top: -6px; margin-right: 6px; } \
  3746. .highscore .score span { float: right; text-align: right; width: 70px; } \
  3747. .highscore .place span { float: right; text-align: right; width: 30px; } \
  3748. .highscore th:nth-child(4) { width: 30% !important; } \
  3749. .highscore th:nth-child(5) { width: 10% !important; } \
  3750. #tab_highscore .centerButton { margin: 10px 0px; }",
  3751. 'memberInfo', true
  3752. );
  3753. },
  3754.  
  3755. /**
  3756. * Starts the script.
  3757. */
  3758. show: function() {
  3759. // Show the link for the info.
  3760. this.addShowButton();
  3761.  
  3762. // Show the info if the flag is set.
  3763. if(myGM.getValue('memberInfo_infoLinkClicked', false)) {
  3764. // Delete the flag.
  3765. myGM.setValue('memberInfo_infoLinkClicked', false);
  3766.  
  3767. // Show the information.
  3768. this.showInfo();
  3769. }
  3770. },
  3771.  
  3772. /**
  3773. * Add a button for showing the inforation to the highscore.
  3774. */
  3775. addShowButton: function() {
  3776. // Get the parent of the show button.
  3777. var parent = myGM.$('#tab_highscore .centerButton');
  3778.  
  3779. // Add the button to show the information.
  3780. var btn = myGM.addElement('input', parent, 'showInfo', 'button');
  3781. btn.type = 'button';
  3782. btn.value = Language.$('memberInfo_show');
  3783.  
  3784. // Add the event listener to the button.
  3785. btn.addEventListener('click', EventHandling.memberInfo.clickShow, false);
  3786. },
  3787.  
  3788. /**
  3789. * Add a button for resetting the inforation to the highscore.
  3790. */
  3791. addResetButton: function() {
  3792. // Get the parent of the reset button.
  3793. var parent = myGM.$('#tab_highscore .content p');
  3794.  
  3795. // Add the reset button.
  3796. var btn = myGM.addElement('input', parent, 'resetInfo', 'button');
  3797. btn.type = 'button';
  3798. btn.value = Language.$('memberInfo_reset');
  3799.  
  3800. // Add the event listener to the button.
  3801. btn.addEventListener('click', EventHandling.memberInfo.clickReset, false);
  3802. },
  3803.  
  3804. /**
  3805. * Adds the time since the last reset to the highscore view.
  3806. */
  3807. addLastResetTime: function() {
  3808. // Get the time since the last reset.
  3809. var lastResetTime = myGM.getValue(General.getServerCode() + '_memberInfo_time_' + MemberInfo.type, 0);
  3810. var diff = new Date() - lastResetTime;
  3811.  
  3812. // Get the hours, days and minutes since the last reset.
  3813. var d = Math.floor(diff / 86400000);
  3814. diff = diff % 86400000;
  3815. var h = Math.floor(diff / 3600000);
  3816. diff = diff % 3600000;
  3817. var m = Math.floor(diff / 60000);
  3818.  
  3819. // Get the string of the last reset.
  3820. var lastReset = lastResetTime ? (d + 'd ' + h + 'h ' + m + 'min') : Language.$('memberInfo_noReset');
  3821.  
  3822. // Get the parent of the reset button.
  3823. var parent = myGM.$('#tab_highscore .content p');
  3824.  
  3825. // Add the reset button.
  3826. var span = myGM.addElement('span', parent);
  3827. span.innerHTML = '<br><span class="bold brown">' + Language.$('memberInfo_lastReset') + ' ' + lastReset + '</span>';
  3828. },
  3829.  
  3830. /**
  3831. * Shows the information about the members in the highscore view.
  3832. */
  3833. showInfo: function() {
  3834. // Set the highscore type.
  3835. this.type = General.getSelectValue('js_highscoreType', true);
  3836.  
  3837. // Storage for member information.
  3838. var memberInfoData = {};
  3839.  
  3840. // Storage for old member information.
  3841. var memberInfoDataOld = myGM.getValue(General.getServerCode() + '_memberInfo_data_' + this.type, null);
  3842.  
  3843. // Get the table rows of the ally members.
  3844. var allyRows = myGM.$$('table.highscore tr.ownally');
  3845.  
  3846. // Loop over all ally rows.
  3847. for(var i = 0; i < allyRows.length; i++) {
  3848. // Get the id of the ally member.
  3849. var actionLink = myGM.$('.action a', allyRows[i]).href;
  3850. var id = actionLink.match(/receiverId=([0-9]*)/i)[1];
  3851.  
  3852. // Get score and place of the member and store it.
  3853. memberInfoData[id] = {};
  3854. memberInfoData[id]['place'] = General.getInt(myGM.$('.place', allyRows[i]).innerHTML);
  3855. memberInfoData[id]['score'] = General.getInt(myGM.$('.score', allyRows[i]).innerHTML);
  3856.  
  3857. // Get the difference in place and score.
  3858. var placeDiff = myGM.addElement('span', myGM.$('.place', allyRows[i]));
  3859. var scoreDiff = myGM.addElement('span', myGM.$('.score', allyRows[i]));
  3860.  
  3861. // Show the difference in place and score.
  3862. placeDiff.innerHTML += (memberInfoDataOld && memberInfoDataOld[id]) ? General.formatToIkaNumber(memberInfoDataOld[id]['place'] - memberInfoData[id]['place'], true, true) : '-';
  3863. scoreDiff.innerHTML += (memberInfoDataOld && memberInfoDataOld[id]) ? General.formatToIkaNumber(memberInfoData[id]['score'] - memberInfoDataOld[id]['score'], true, true) : '-';
  3864. }
  3865.  
  3866. // Get the own table row.
  3867. var ownRow = myGM.$('table.highscore tr.own');
  3868.  
  3869. // Get the own score and place.
  3870. memberInfoData['own'] = {};
  3871. memberInfoData['own']['place'] = General.getInt(myGM.$('.place', ownRow).innerHTML);
  3872. memberInfoData['own']['score'] = General.getInt(myGM.$('.score', ownRow).innerHTML);
  3873.  
  3874. // Get the difference in place and score.
  3875. var placeDiff = myGM.addElement('span', myGM.$('.place', ownRow));
  3876. var scoreDiff = myGM.addElement('span', myGM.$('.score', ownRow));
  3877.  
  3878. // Show the difference in place and score.
  3879. placeDiff.innerHTML += (memberInfoDataOld && memberInfoDataOld['own']) ? General.formatToIkaNumber(memberInfoDataOld['own']['place'] - memberInfoData['own']['place'], true, true) : '-';
  3880. scoreDiff.innerHTML += (memberInfoDataOld && memberInfoDataOld['own']) ? General.formatToIkaNumber(memberInfoData['own']['score'] - memberInfoDataOld['own']['score'], true, true) : '-';
  3881.  
  3882. // Store the new member information for later use.
  3883. this.data = memberInfoData;
  3884.  
  3885. // Add the reset button.
  3886. this.addResetButton();
  3887.  
  3888. // Show the time of the last reset.
  3889. this.addLastResetTime();
  3890. }
  3891. };
  3892.  
  3893. /**
  3894. * Functions for messages.
  3895. */
  3896. Message = {
  3897. /**
  3898. * Add a signature to each message.
  3899. */
  3900. addSignature: function() {
  3901. // Storage for the signature text.
  3902. var signature = '';
  3903. // If the server signature should be used, get the server signature text.
  3904. if(myGM.getValue('messageSignature_useServerSignature_' + General.getServerCode(), false)) {
  3905. signature = myGM.getValue('messageSignature_serverSignature_' + General.getServerCode(), '');
  3906.  
  3907. // Otherwise: get the global signature text.
  3908. } else {
  3909. signature = myGM.getValue('messageSignature_globalSignature', '');
  3910. }
  3911.  
  3912. // If a signature text is set, add it to the message.
  3913. if(signature != '') {
  3914. // Get the message area.
  3915. var messageArea = myGM.$('#js_msgTextConfirm');
  3916.  
  3917. // Get the message text and add the signature on the right placement.
  3918. var text = messageArea.value;
  3919. text = myGM.getValue('messageSignature_placementTop', true) ? ('\n\n' + signature + text) : (text + '\n\n' + signature);
  3920. messageArea.value = text;
  3921.  
  3922. // Focus the message area on top.
  3923. messageArea.setSelectionRange(0,0);
  3924. messageArea.focus();
  3925. }
  3926. },
  3927.  
  3928. /**
  3929. * Add a link for circular messages to the toolbar.
  3930. */
  3931. easyCircularMessage: function() {
  3932. // Get the script toolbar.
  3933. var scriptToolbar = myGM.$('#' + myGM.prefix + 'toolbar');
  3934.  
  3935. // Add the message link wrapper.
  3936. var circularMessageLinkWrapper = myGM.addElement('div', scriptToolbar, 'circularMessageLinkWrapper');
  3937. // Prepare the message id, link, class and title.
  3938. var id = myGM.prefix + 'circularMessageLink';
  3939. var href = '?view=sendIKMessage&msgType=51&allyId=' + ika.getModel().avatarAllyId;
  3940. var cl = href.match(/diplomacyAlly/) ? 'notSet' : '';
  3941. var title = href.match(/diplomacyAlly/) ? Language.$('easyCircularMsg_getLink') : Language.$('easyCircularMsg_send');
  3942.  
  3943. // Add the message link (workaround for ajaxHandlerCall).
  3944. circularMessageLinkWrapper.innerHTML = '<a id="' + id + '" class="' + cl + '" href="' + href + '" title="' + title + '" onclick="ajaxHandlerCall(this.href); return false;"></a>';
  3945.  
  3946. // Set the styles.
  3947. myGM.addStyle(
  3948. "#" + myGM.prefix + "circularMessageLinkWrapper { position: absolute; top: 5px; right: 70px; } \
  3949. #" + myGM.prefix + "circularMessageLink { height: 9px; width: 13px; display: block; margin: 0px !important; background: url('skin/interface/icon_send_message.png') repeat scroll 0 0 transparent; } \
  3950. #" + myGM.prefix + "circularMessageLink:hover { background-position: 0px -9px; } \
  3951. #" + myGM.prefix + "circularMessageLink.notSet:hover { background-position: 0px -18px; }",
  3952. 'easyCircularMessage', true);
  3953. },
  3954.  
  3955. /**
  3956. * Set the style for the replaced urls.
  3957. */
  3958. setStyleForReplaceUrl: function() {
  3959. // Add the style.
  3960. myGM.addStyle(
  3961. "." + myGM.prefix + "replacedUrl { font-weight: bold; font-style: italic; } \
  3962. ." + myGM.prefix + "replacedUrl:hover { text-decoration: underline; cursor: pointer; }"
  3963. );
  3964. },
  3965.  
  3966. /**
  3967. * Make all URLs in messsages clickable. But with a security check.
  3968. */
  3969. replaceUrl: function() {
  3970. // Get all message texts.
  3971. var msgTexts = myGM.$$('.msgText');
  3972.  
  3973. // Loop over all message texts.
  3974. for(var i = 0; i < msgTexts.length; i++) {
  3975. // Replace the URLs.
  3976. var text = msgTexts[i].innerHTML;
  3977. msgTexts[i].innerHTML = text.replace(/(?:^|\s)(http(s?)\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(\/[^<\s]*)?)/g, ' <span class="' + myGM.prefix + 'replacedUrl" title="$1">$1</span> ');
  3978. }
  3979.  
  3980. // Get all replaced URLs.
  3981. var replacedUrls = myGM.$$('.' + myGM.prefix + 'replacedUrl');
  3982.  
  3983. // Loop over all replaced URLs.
  3984. for(var i = 0; i < replacedUrls.length; i++) {
  3985. // Add a click event to each replaced URL.
  3986. replacedUrls[i].addEventListener('click', EventHandling.replacedUrl.click, true);
  3987. }
  3988. }
  3989. };
  3990.  
  3991. /**
  3992. * Functions for cities.
  3993. */
  3994. City = {
  3995. /**
  3996. * Set the links for cities that are colonizing.
  3997. */
  3998. setColonizingLinks: function() {
  3999. // Get all cities with level 0 (colonizing cities).
  4000. var colonizingCities = myGM.$$('.level0');
  4001.  
  4002. // Loop over the colonizing cities.
  4003. for(var i = 0; i < colonizingCities.length; i++) {
  4004. // Get the location id.
  4005. var locationId = colonizingCities[i].id.replace(/\D/g, '');
  4006.  
  4007. // Get the city id.
  4008. var cityId = ika.getScreen().data.cities[locationId].id;
  4009.  
  4010. // Set the city link.
  4011. myGM.$('#js_cityLocation' + locationId + 'Link').href = '?view=cityDetails&destinationCityId=' + cityId;
  4012. }
  4013. }
  4014. };
  4015.  
  4016. /**
  4017. * Functions for nice formatting of units.
  4018. */
  4019. UnitInfo = {
  4020. /**
  4021. * Add a link to the troops in town view to get a list for sharing.
  4022. */
  4023. addPopupLink: function() {
  4024. var infoButton = myGM.addElement('input', myGM.$('#cityMilitary_c .buildingDescription'), null, 'button', new Array(['position', 'absolute'], ['top', '5px'], ['right', '20px']));
  4025. infoButton.type = 'button';
  4026. infoButton.value = Language.$('unitInfo_button');
  4027. infoButton.addEventListener('click', EventHandling.unitInfo.click, true);
  4028. },
  4029. /**
  4030. * Shows a popup with the unit information.
  4031. */
  4032. showPopup: function() {
  4033. // Get information about the town.
  4034. var selected = ika.getModel().relatedCityData[ika.getModel().relatedCityData.selectedCity];
  4035. var townInfo = selected.name + ' ' + selected.coords;
  4036. // Storage for output.
  4037. var output = '<textarea readonly="readonly">';
  4038. // Get the troops and ships.
  4039. var troopList = this.getOutput('Units');
  4040. var shipList = this.getOutput('Ships');
  4041. // Add the troop list to the output, if filled.
  4042. if(troopList.length > 0) {
  4043. output += '===== ' + Language.$('unitInfo_units_label') + townInfo + ' =====' + troopList;
  4044. // If there is also a shiplist, add a seperator.
  4045. output += (shipList.length > 0) ? '\n\n' + '-'.repeat(85) + '\n\n' : '';
  4046. }
  4047. // Add the ship list to the output, if filled. {
  4048. output += (shipList.length > 0) ? '===== ' + Language.$('unitInfo_ships_label') + townInfo + ' =====' + shipList : '';
  4049. // Output should be in a textarea.
  4050. output += '</textarea>';
  4051. // Prepare the output.
  4052. var text = {
  4053. header: Language.$('unitInfo_header') + townInfo,
  4054. body: output
  4055. };
  4056. // Show the output.
  4057. var notificationId = myGM.notification(text);
  4058. // Set the style for the textarea.
  4059. myGM.addStyle(
  4060. "#" + myGM.prefix + "notificationPanelBodyMContent" + notificationId + " textarea { width: 100%; height: 260px; resize: none;",
  4061. 'unitInfoTextarea', true
  4062. );
  4063. // Add a the listener to select the information on focus.
  4064. myGM.$('#' + myGM.prefix + 'notificationPanelBodyMContent' + notificationId + ' textarea').addEventListener('focus', function() { this.select(); }, false);
  4065. },
  4066. /**
  4067. * Gets the units in a wrapper and returns them as a list.
  4068. *
  4069. * @param String id
  4070. * The id of the tab.
  4071. *
  4072. * @return String
  4073. * The value for this tab.
  4074. */
  4075. getOutput: function(id) {
  4076. // Get the own units and store them.
  4077. var ret = this.getOwnUnits(id);
  4078. // Status of foreign troops.
  4079. var foreignStatus = new Array('friends', 'enemies');
  4080. // Get all wrapper for troops.
  4081. var wrapper = myGM.$$('#tab' + id + ' .contentBox01h');
  4082. // Loop over all wrappers.
  4083. for(var i = 0; i < foreignStatus.length; i++) {
  4084. // Get the foreign units for this status and store them.
  4085. var foreignUnits = this.getForeignUnits(wrapper[i + 1], id, foreignStatus[i]);
  4086. ret += (foreignUnits.length > 0) ? (((ret.length > 0) ? '\n' : '') + foreignUnits) : '';
  4087. }
  4088. // Return the output.
  4089. return ret;
  4090. },
  4091. /**
  4092. * Get the own units in the town.
  4093. *
  4094. * @param String id
  4095. * The id of the tab.
  4096. *
  4097. * @return String
  4098. * The list of own units.
  4099. */
  4100. getOwnUnits: function(id) {
  4101. // Get the wrapper.
  4102. var wrapper = myGM.$('#tab' + id + ' .contentBox01h');
  4103. // Get the name and number cells.
  4104. var nameCells = myGM.$$('.table01 .title_img_row th', wrapper);
  4105. var numberCells = myGM.$$('.table01 .count td', wrapper);
  4106. // Storage for the list.
  4107. var list = '';
  4108. // Add the units if their numer is not zero.
  4109. for(var i = 0; i < nameCells.length; i++) {
  4110. var name = nameCells[i].title;
  4111. var number = General.getInt(numberCells[i].innerHTML);
  4112. list += (number > 0) ? '\n' + name + ': ' + number : '';
  4113. }
  4114. // Get the headline.
  4115. var headline = '\n--- ' + Language.$('unitInfo_' + id.toLowerCase() + '_own') + ' ---';
  4116. // Return the list with headline if some units were found. Otherwise return an empty string.
  4117. return (list.length > 0) ? (headline + list) : '';
  4118. },
  4119. /**
  4120. * Get the foreign units in the town with the status.
  4121. *
  4122. * @param String wrapper
  4123. * The wrapper which contains the foreign units.
  4124. * @param String id
  4125. * The id of the tab.
  4126. * @param String status
  4127. * The status of the units.
  4128. *
  4129. * @return String
  4130. * The list of foreign units.
  4131. */
  4132. getForeignUnits: function(wrapper, id, status) {
  4133. // Get the name cells and number rows.
  4134. var nameCells = myGM.$$('.table01 .title_img_row th:not(:first-child)', wrapper);
  4135. var numberRows = myGM.$$('.table01 tr:not(.title_img_row)', wrapper);
  4136. // Distance between two belonging rows and storage for return value.
  4137. var distance = numberRows.length / 2;
  4138. var ret = '';
  4139. // Iterate over all number rows.
  4140. for(var i = 0; i < distance; i++) {
  4141. // Get the number cells and the player name.
  4142. var numberCells = myGM.$$('td:not(:first-child)', numberRows[i]).concat(myGM.$$('td:not(:first-child)', numberRows[i + distance]));
  4143. var playerName = myGM.$('td a', numberRows[i]).innerHTML;
  4144. // Storage for the list.
  4145. var list = '';
  4146. // Add the units if their numer is not zero.
  4147. for(var k = 0; k < numberCells.length; k++) {
  4148. var name = nameCells[k].title;
  4149. var number = General.getInt(numberCells[k].innerHTML);
  4150. list += (number > 0) ? '\n' + name + ': ' + number : '';
  4151. }
  4152. // Add the units with the player name to the list, if some units were found.
  4153. list = ((list.length > 0) ? '\n* ' + playerName + ' *' : '') + list;
  4154. ret += ((list.length > 0 && ret.length > 0) ? '\n' : '') + list;
  4155. }
  4156. // Get the headline.
  4157. var headline = '\n--- ' + Language.$('unitInfo_' + id.toLowerCase() + '_' + status) + ' ---';
  4158. // Return the list with headline if some units were found. Otherwise return an empty string.
  4159. return (ret.length > 0) ? (headline + ret) : '';
  4160. }
  4161. };
  4162.  
  4163. /**
  4164. * The main function of the script.
  4165. */
  4166. function main() {
  4167. // Init the myGM functions.
  4168. myGM.init();
  4169.  
  4170. // If the script was already executed, do nothing.
  4171. if(myGM.$('#' + myGM.prefix + 'alreadyExecutedScript')) return;
  4172.  
  4173. // Add the hint, that the script was already executed.
  4174. var alreadyExecuted = myGM.addElement('input', myGM.$('#container'), 'alreadyExecutedScript');
  4175. alreadyExecuted.type = 'hidden';
  4176.  
  4177. // Init the language.
  4178. Language.init();
  4179.  
  4180. // Init the script.
  4181. General.init();
  4182.  
  4183. // Call the function to check for updates.
  4184. Updater.init();
  4185.  
  4186. // Call the function to enhance the view.
  4187. EnhancedView.init();
  4188. }
  4189.  
  4190. // Call the main function of the script.
  4191. setTimeout(main, 0);