Greasy Fork is available in English.

Ikariam Core

Framework for Ikariam userscript developers.

Ovu skriptu ne treba izravno instalirati. To je biblioteka za druge skripte koje se uključuju u meta direktivu // @require https://update.greatest.deepsurf.us/scripts/5574/961702/Ikariam%20Core.js

  1. // ==UserScript==
  2. // @name Ikariam Core
  3. // @description Framework for Ikariam userscript developers.
  4. // @namespace IkariamCore
  5. // @author Tobbe
  6. // @version 3.2.1
  7. // @license MIT License
  8. //
  9. // @name:de Ikariam Core
  10. // @description:de Framework für Ikariam Benutzerscript Entwickler.
  11. //
  12. // @exclude *
  13. //
  14. // @connect greatest.deepsurf.us
  15. //
  16. // @grant unsafeWindow
  17. // @grant GM_setValue
  18. // @grant GM.setValue
  19. // @grant GM_getValue
  20. // @grant GM.getValue
  21. // @grant GM_deleteValue
  22. // @grant GM.deleteValue
  23. // @grant GM_listValues
  24. // @grant GM.listValues
  25. // @grant GM_getResourceText
  26. // @grant GM.getResourceText
  27. // @grant GM_xmlhttpRequest
  28. // @grant GM.xmlHttpRequest
  29. // ==/UserScript==
  30.  
  31. // Add some functions to the String and Array prototype, namespaced by "IC".
  32. (function() {
  33. // "Internal" function to add functions to native objects with the usage of an namespace.
  34. var _addNamespacedFunctions = function(io_parent, is_namespaceName, io_functionsToAdd) {
  35. var lo_functionStorage = {};
  36. var lf_createGetter = function(is_functionName) {
  37. return function() {
  38. return function() {
  39. return io_functionsToAdd[is_functionName].apply(go_self, arguments);
  40. };
  41. };
  42. };
  43. for(var ls_functionName in io_functionsToAdd) {
  44. if(io_functionsToAdd.hasOwnProperty(ls_functionName)) {
  45. Object.defineProperty(lo_functionStorage, ls_functionName, { get: lf_createGetter(ls_functionName) });
  46. }
  47. }
  48. Object.defineProperty(io_parent.prototype, is_namespaceName, {
  49. get: function() {
  50. go_self = this;
  51. return lo_functionStorage;
  52. }
  53. });
  54. };
  55. /**
  56. * Additional methods for processing strings.
  57. *
  58. * @external "String.IC"
  59. */
  60. _addNamespacedFunctions(String, 'IC', {
  61. /**
  62. * Replaces characters or whitespaces at the beginning of a string.
  63. *
  64. * @function external:"String.IC".ltrim
  65. *
  66. * @param {?String} [is_toRemove=whitespaces]
  67. * A string containing the characters to remove.
  68. *
  69. * @return {String}
  70. * The trimmed string.
  71. */
  72. ltrim: function(is_toRemove) {
  73. return !!is_toRemove ? this.replace(new RegExp('^[' + is_toRemove + ']+'), '') : this.replace(/^\s+/, '');
  74. },
  75. /**
  76. * Replaces characters or whitespaces at the end of a string.
  77. *
  78. * @function external:"String.IC".rtrim
  79. *
  80. * @param {?String} [is_toRemove=whitespaces]
  81. * A string containing the characters to remove.
  82. *
  83. * @return {String}
  84. * The trimmed string.
  85. */
  86. rtrim: function(is_toRemove) {
  87. return !!is_toRemove ? this.replace(new RegExp('[' + is_toRemove + ']+$'), '') : this.replace(/\s+$/, '');
  88. },
  89. /**
  90. * Replaces characters or whitespaces at the beginning and end of a string.
  91. *
  92. * @function external:"String.IC".trim
  93. *
  94. * @param {?String} [is_toRemove=whitespaces]
  95. * A string containing the characters to remove.
  96. *
  97. * @return {String}
  98. * The trimmed string.
  99. */
  100. trim: function(is_toRemove) {
  101. return this.IC.ltrim(is_toRemove).IC.rtrim(is_toRemove);
  102. },
  103. /**
  104. * Encodes HTML-special characters in a string.
  105. *
  106. * @function external:"String.IC".encodeHTML
  107. *
  108. * @return {String}
  109. * The encoded string.
  110. */
  111. encodeHTML: function() {
  112. // Set the characters to encode.
  113. var lo_characters = {
  114. '&': '&',
  115. '"': '"',
  116. '\'': ''',
  117. '<': '&lt;',
  118. '>': '&gt;'
  119. };
  120. return this.replace(/([\&"'<>])/g, function(is_string, is_symbol) { return lo_characters[is_symbol]; });
  121. },
  122. /**
  123. * Decodes HTML-special characters in a string.
  124. *
  125. * @function external:"String.IC".decodeHTML
  126. *
  127. * @return {String}
  128. * The decoded string.
  129. */
  130. decodeHTML: function() {
  131. // Set the characters to decode.
  132. var lo_characters = {
  133. '&amp;': '&',
  134. '&quot;': '"',
  135. '&apos;': '\'',
  136. '&lt;': '<',
  137. '&gt;': '>'
  138. };
  139. return this.replace(/(&quot;|&apos;|&lt;|&gt;|&amp;)/g, function(is_string, is_symbol) { return lo_characters[is_symbol]; });
  140. },
  141. /**
  142. * Repeats a string a specified number of times.
  143. *
  144. * @function external:"String.IC".repeat
  145. *
  146. * @param {int} ii_nr
  147. * The number of times to repeat the string.
  148. *
  149. * @return {String}
  150. * The repeated string.
  151. */
  152. repeat: function(ii_nr) {
  153. var rs_repeated = this;
  154. for(var i = 1; i < ii_nr; i++) {
  155. rs_repeated += this;
  156. }
  157. return rs_repeated;
  158. }
  159. });
  160. /**
  161. * Additional methods for processing arrays.
  162. *
  163. * @external "Array.IC"
  164. */
  165. _addNamespacedFunctions(Array, 'IC', {
  166. /**
  167. * Inserts an element at a specified position into an array.
  168. *
  169. * @function external:"Array.IC".insert
  170. *
  171. * @param {*} im_item
  172. * The item which should be inserted.
  173. * @param {?int} [ii_index=this.length]
  174. * The position where the element should be added. If not set, the element will be added at the end.
  175. */
  176. insert: function(im_item, ii_index) {
  177. var li_maxIndex = this.length;
  178. // Get the index to insert.
  179. var li_index = !ii_index && ii_index != 0 ? li_maxIndex : ii_index;
  180. li_index = Math.max(li_index, 0); // No negative index.
  181. li_index = Math.min(li_index, li_maxIndex); // No index bigger than the array length.
  182. this.splice(li_index, 0, im_item);
  183. },
  184. /**
  185. * Deletes an element at a specified position from an array.
  186. *
  187. * @function external:"Array.IC".remove
  188. *
  189. * @param {int} ii_index
  190. * The position of the element which should be deleted.
  191. */
  192. remove: function(ii_index) {
  193. if(ii_index >= 0 && ii_index < this.length) {
  194. this.splice(ii_index, 1);
  195. }
  196. }
  197. });
  198. /**
  199. * Additional methods for processing dates.
  200. *
  201. * @external "Date.IC"
  202. */
  203. _addNamespacedFunctions(Date, 'IC', {
  204. /**
  205. * Formats a date / time.
  206. *
  207. * @example
  208. * (new Date()).IC.format('yyyy-MM-dd HH:mm:ss.SSS');
  209. *
  210. * @function external:"Date.IC".format
  211. *
  212. * @param {String} is_pattern
  213. * The pattern for the output.<br>
  214. * <br>
  215. * Options:<br>
  216. * <pre> yyyy year, four digits
  217. * yy year, two digits
  218. * MM month, leading 0
  219. * M month, no leading 0
  220. * dd day, leading 0
  221. * d day, no leading 0
  222. * hh hour, 1-12, leading 0
  223. * h hour, 1-12, no leading 0
  224. * HH hour, 0-23, leading 0
  225. * H hour, 0-23, no leading 0
  226. * mm minute, leading 0
  227. * m minute, no leading 0
  228. * ss seconds, leading 0
  229. * s seconds, no leading 0
  230. * SSS milliseconds, leading 0
  231. * S milliseconds, no leading 0
  232. * a AM / PM</pre>
  233. */
  234. format: function(is_pattern) {
  235. var lo_possibleOptions = {
  236. 'yyyy': this.getFullYear(), // year, four digits
  237. 'yy': this.getYear() % 100, // year, two digits
  238. 'MM': this.getMonth() + 1, // month, leading 0
  239. 'M': this.getMonth() + 1, // month, no leading 0
  240. 'dd': this.getDate(), // day, leading 0
  241. 'd': this.getDate(), // day, no leading 0
  242. 'hh': this.getHours() + 1, // hour, 1-12, leading 0
  243. 'h': this.getHours() + 1, // hour, 1-12, no leading 0
  244. 'HH': this.getHours(), // hour, 0-23, leading 0
  245. 'H': this.getHours(), // hour, 0-23, no leading 0
  246. 'mm': this.getMinutes(), // minute, leading 0
  247. 'm': this.getMinutes(), // minute, no leading 0
  248. 'ss': this.getSeconds(), // seconds, leading 0
  249. 's': this.getSeconds(), // seconds, no leading 0
  250. 'SSS': this.getMilliseconds(), // milliseconds, ledaing 0
  251. 'S': this.getMilliseconds(), // milliseconds, no leading 0
  252. 'a': 'AM' // AM / PM
  253. };
  254. if(lo_possibleOptions.MM < 10) lo_possibleOptions.MM = '0' + lo_possibleOptions.MM;
  255. if(lo_possibleOptions.dd < 10) lo_possibleOptions.dd = '0' + lo_possibleOptions.dd;
  256. if(lo_possibleOptions.h > 12) lo_possibleOptions.hh = lo_possibleOptions.h = lo_possibleOptions.h - 12;
  257. if(lo_possibleOptions.hh < 10) lo_possibleOptions.hh = '0' + lo_possibleOptions.hh;
  258. if(lo_possibleOptions.HH < 10) lo_possibleOptions.HH = '0' + lo_possibleOptions.HH;
  259. if(lo_possibleOptions.mm < 10) lo_possibleOptions.mm = '0' + lo_possibleOptions.mm;
  260. if(lo_possibleOptions.ss < 10) lo_possibleOptions.ss = '0' + lo_possibleOptions.ss;
  261. if(lo_possibleOptions.S < 100) lo_possibleOptions.SSS = '0' + lo_possibleOptions.SSS;
  262. if(lo_possibleOptions.S < 10) lo_possibleOptions.SSS = '0' + lo_possibleOptions.SSS;
  263. if(lo_possibleOptions.H > 11) lo_possibleOptions.a = 'PM';
  264. var rs_pattern = is_pattern;
  265. for(var ls_option in lo_possibleOptions) {
  266. rs_pattern = rs_pattern.replace(new RegExp('(' + ls_option + ')', 'g'), lo_possibleOptions[ls_option]);
  267. }
  268. return rs_pattern;
  269. }
  270. });
  271. })();
  272.  
  273. /**
  274. * Instantiate a new set of core functions.<br>
  275. * {@link https://greatest.deepsurf.us/scripts/5574-ikariam-core Script on Greasy Fork}<br>
  276. * {@link https://github.com/IkaScripts/IkariamCore Script on GitHub}
  277. *
  278. * @version 3.2.1
  279. * @author Tobbe <ikascripts@tobbexiv.de>
  280. *
  281. * @global
  282. *
  283. * @class
  284. * @classdesc Framework for Ikariam userscript developers.
  285. *
  286. * @param {String} is_scriptVersion
  287. * The version of the script using Ikariam Core.
  288. * @param {int} ii_scriptId
  289. * The id of the script using Ikariam Core.
  290. * @param {String} is_scriptName
  291. * The name of the script using Ikariam Core.
  292. * @param {String} is_scriptAuthor
  293. * The author of the script using Ikariam Core.
  294. * @param {boolean} ib_debug
  295. * If debugging is enabled.
  296. */
  297. function IkariamCore(is_scriptVersion, ii_scriptId, is_scriptName, is_scriptAuthor, ib_debug) {
  298. /**
  299. * Storage for accessing <code>this</code> as reference to IkariamCore in subfunctions. Do <b>NOT</b> delete!
  300. *
  301. * @private
  302. * @inner
  303. *
  304. * @type IkariamCore
  305. */
  306. var go_self = this;
  307. /**
  308. * Storage for information on the script using Ikariam Core.
  309. *
  310. * @private
  311. * @inner
  312. *
  313. * @type Object
  314. *
  315. * @property {String} version - The script version.
  316. * @property {int} id - The script id.
  317. * @property {String} name - The script name.
  318. * @property {String} author - The script author.
  319. */
  320. var go_script = {
  321. version: is_scriptVersion,
  322. id: ii_scriptId,
  323. name: is_scriptName,
  324. author: is_scriptAuthor
  325. };
  326. /**
  327. * General settings like debugging switched on / off.
  328. *
  329. * @private
  330. * @inner
  331. *
  332. * @type Object
  333. *
  334. * @property {boolean} debug - If debugging is enabled.
  335. */
  336. var go_settings = {
  337. debug: ib_debug
  338. };
  339. /**
  340. * A reference to <code>window</code> / <code>unsafeWindow</code>.
  341. *
  342. * @instance
  343. *
  344. * @type window
  345. */
  346. this.win = typeof unsafeWindow != 'undefined' ? unsafeWindow : window;
  347. /**
  348. * Reference to <code>window.ikariam</code>.
  349. *
  350. * @instance
  351. *
  352. * @type Object
  353. */
  354. this.ika = this.win.ikariam;
  355. /**
  356. * Debugging console.<br>
  357. * Available commands:<br>
  358. * <code>assert, clear, count, debug, dir, dirxml, error, exception, group, groupCollapsed, groupEnd,
  359. * info, log, logTimeStamp, profile, profileEnd, table, time, timeEnd, timeStamp, trace, warn</code><br>
  360. * <br>
  361. * The console is deactivated by the Ikariam page but with the script {@link https://greatest.deepsurf.us/de/scripts/6310-rescue-console Rescue Console} you can use it.
  362. *
  363. * @instance
  364. *
  365. * @type console
  366. */
  367. this.con = (function() {
  368. // Wrapper for console functions.
  369. var lo_consoleWrapper = {};
  370. // Set the console to the "rescued" debugConsole.
  371. var lo_originalConsole = go_self.win.debugConsole;
  372. // Define all console tags.
  373. var la_tags = ['assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error', 'exception',
  374. 'group', 'groupCollapsed', 'groupEnd', 'info', 'log', 'logTimeStamp', 'profile',
  375. 'profileEnd', 'table', 'time', 'timeEnd', 'timeStamp', 'trace', 'warn'];
  376. var lo_counters = {};
  377. var lo_timers = {};
  378. // Define the backup functions.
  379. var lo_selfDefinedFunctions = {
  380. assert: function(im_toCheck, im_toLog) {
  381. if(im_toCheck === false || im_toCheck === 0 || im_toCheck === null || im_toCheck === undefined) {
  382. go_self.con.error(im_toLog || 'Assertion Failure');
  383. }
  384. },
  385. count: function(is_name) {
  386. if(!lo_counters[is_name] === true)
  387. lo_counters[is_name] = 0;
  388. lo_counters[is_name]++;
  389. go_self.con.log(is_name + ': ' + lo_counters[is_name]);
  390. },
  391. debug: function() {
  392. go_self.con.log.apply(arguments);
  393. },
  394. error: function() {
  395. go_self.con.log.apply(arguments);
  396. },
  397. exception: function() {
  398. go_self.con.log.apply(arguments);
  399. },
  400. info: function() {
  401. go_self.con.log.apply(arguments);
  402. },
  403. logTimeStamp: function(iv_name) {
  404. go_self.con.log((new Date()).IC.format('HH:mm:ss.SSS') + ' ' + iv_name);
  405. },
  406. time: function(is_name) {
  407. go_self.con.info(is_name + ': timer started');
  408. lo_timers[is_name] = new Date();
  409. },
  410. timeEnd: function(is_name) {
  411. var ld_now = new Date();
  412. var li_timeElapsed = ld_now.getMilliseconds() - lo_timers[is_name].getMilliseconds();
  413. delete lo_timers[is_name];
  414. go_self.con.info(is_name + ': ' + li_timeElapsed + 'ms');
  415. },
  416. warn: function() {
  417. go_self.con.log.apply(arguments);
  418. }
  419. };
  420. for(var i = 0; i < la_tags.length; i++) {
  421. var ls_key = la_tags[i];
  422. if(go_settings.debug) {
  423. // If available in console: use console; else: use backup function if available.
  424. if(lo_originalConsole[ls_key]) {
  425. lo_consoleWrapper[ls_key] = lo_originalConsole[ls_key];
  426. } else if(lo_selfDefinedFunctions[ls_key]) {
  427. lo_consoleWrapper[ls_key] = lo_selfDefinedFunctions[ls_key];
  428. }
  429. }
  430. // If the function is not set yet, set it to an empty function.
  431. if(!lo_consoleWrapper[ls_key]) {
  432. lo_consoleWrapper[ls_key] = function() { return; };
  433. }
  434. }
  435. return lo_consoleWrapper;
  436. })();
  437.  
  438. this.con.groupCollapsed('IkariamCore initalization ...');
  439. /**
  440. * Instantiate a new set of myGM functions.
  441. *
  442. * @inner
  443. *
  444. * @class
  445. * @classdesc Functions for cross-browser compatibility of the GM_* functions.<br>Also there are some new functionalities implemented.
  446. */
  447. function myGM() {
  448. /*--------------------------------------------*
  449. * Private variables, functions and settings. *
  450. *--------------------------------------------*/
  451. /**
  452. * Storage for style sheets which will be added by the script.
  453. *
  454. * @private
  455. * @inner
  456. *
  457. * @type Object.<String, Element>
  458. */
  459. var _go_styleSheets = {};
  460. /**
  461. * Storage for notification id for possibility to identify a notification popup.
  462. *
  463. * @private
  464. * @inner
  465. *
  466. * @type int
  467. */
  468. var _gi_notificationId = 0;
  469. /**
  470. * If the GM_ functions <code>GM_setValue</code>, <code>GM_getValue</code>, <code>GM_deleteValue</code> and <code>GM_listValues</code> can be used.
  471. *
  472. * @private
  473. * @inner
  474. *
  475. * @type boolean
  476. */
  477. var _gb_canUseGmStorage = !(typeof GM_getValue == 'undefined')
  478. && !(typeof GM_setValue == 'undefined')
  479. && !(typeof GM_deleteValue == 'undefined')
  480. && !(typeof GM_listValues == 'undefined');
  481. /**
  482. * If the GM. functions <code>GM.setValue</code>, <code>GM.getValue</code>, <code>GM.deleteValue</code> and <code>GM.listValues</code> can be used.
  483. *
  484. * @private
  485. * @inner
  486. *
  487. * @type boolean
  488. */
  489. var _gb_canUseGmStorageNew = !(typeof GM == 'undefined')
  490. && !(typeof GM.getValue == 'undefined')
  491. && !(typeof GM.setValue == 'undefined')
  492. && !(typeof GM.deleteValue == 'undefined')
  493. && !(typeof GM.listValues == 'undefined');
  494. /**
  495. * If the GM_ function <code>GM_getResourceText</code> can be used.
  496. *
  497. * @private
  498. * @inner
  499. *
  500. * @type boolean
  501. */
  502. var _gb_canUseGmRessource = !(typeof GM_getResourceText == 'undefined');
  503. /**
  504. * If the GM_ function <code>GM_xmlhttpRequest</code> can be used.
  505. *
  506. * @private
  507. * @inner
  508. *
  509. * @type boolean
  510. */
  511. var _gb_canUseGmXhr = !(typeof GM_xmlhttpRequest == 'undefined');
  512. /**
  513. * If the GM. function <code>GM.xmlhttpRequest</code> can be used.
  514. *
  515. * @private
  516. * @inner
  517. *
  518. * @type boolean
  519. */
  520. var _gb_canUseGmXhrNew = !(typeof GM == 'undefined') && !(typeof GM.xmlHttpRequest == 'undefined');
  521. /**
  522. * If the local storage can be used.
  523. *
  524. * @private
  525. * @inner
  526. *
  527. * @type boolean
  528. */
  529. var _gb_canUseLocalStorage = !!go_self.win.localStorage;
  530. /**
  531. * The domain for storing cookies.
  532. *
  533. * @private
  534. * @inner
  535. *
  536. * @type String
  537. */
  538. var _gs_cookieDomain = 'ikariam.gameforge.com';
  539. /**
  540. * Create the header for a notification panel.
  541. *
  542. * @private
  543. * @inner
  544. *
  545. * @param {int} ii_id
  546. * The id of the notification.
  547. * @param {Element} ie_panel
  548. * The panel of the notification.
  549. * @param {String} is_headerText
  550. * The text for the header.
  551. * @param {IkariamCore~myGM~ConfirmAbortWithoutInput} if_closePanel
  552. * The function to close the notification panel.
  553. */
  554. var _createNotificationPanelHeader = function(ii_id, ie_panel, is_headerText, if_closePanel) {
  555. var le_wrapper = go_self.myGM.addElement('div', ie_panel, { 'id': 'notificationPanelHeader' + ii_id, 'class': 'notificationPanelHeader' }, true);
  556. var le_left = go_self.myGM.addElement('div', le_wrapper, { 'id': 'notificationPanelHeaderL' + ii_id, 'class': 'notificationPanelHeaderL' }, true);
  557. var le_right = go_self.myGM.addElement('div', le_left, { 'id': 'notificationPanelHeaderR' + ii_id, 'class': 'notificationPanelHeaderR' }, true);
  558. var le_center = go_self.myGM.addElement('div', le_right, { 'id': 'notificationPanelHeaderM' + ii_id, 'class': 'notificationPanelHeaderM', 'innerHTML': is_headerText }, true);
  559. go_self.myGM.addElement('div', le_center, { 'id': 'notificationPanelClose' + ii_id, 'class': 'notificationPanelClose', 'click': if_closePanel }, true);
  560. };
  561. /**
  562. * Create the header for a notification.
  563. *
  564. * @private
  565. * @inner
  566. *
  567. * @param {int} ii_id
  568. * The id of the notification.
  569. * @param {Element} ie_panel
  570. * The panel of the notification.
  571. * @param {IkariamCore~myGM~NotificationBodyOptions} io_options
  572. * Options for the body.<br>
  573. * @param {IkariamCore~myGM~NotificationBodyText} io_texts
  574. * The texts for the body.
  575. */
  576. var _createNotificationPanelBody = function(ii_id, ie_panel, io_options, io_texts) {
  577. var le_wrapper = go_self.myGM.addElement('div', ie_panel, { 'id': 'notificationPanelBody' + ii_id, 'class': 'notificationPanelBody' }, true);
  578. var le_left = go_self.myGM.addElement('div', le_wrapper, { 'id': 'notificationPanelBodyL' + ii_id, 'class': 'notificationPanelBodyL' }, true);
  579. var le_right = go_self.myGM.addElement('div', le_left, { 'id': 'notificationPanelBodyR' + ii_id, 'class': 'notificationPanelBodyR' }, true);
  580. var le_center = go_self.myGM.addElement('div', le_right, { 'id': 'notificationPanelBodyM' + ii_id, 'class': 'notificationPanelBodyM' }, true);
  581. var ls_bodyType = 'div';
  582. var re_body;
  583. var lo_generalOptions = {};
  584. if(io_options.textarea === true) {
  585. ls_bodyType = 'textarea';
  586. if(io_options.readonly === true)
  587. lo_generalOptions['readonly'] = 'readonly';
  588. if(io_options.autoselect === true)
  589. lo_generalOptions['focus'] = function() { this.select(); };
  590. }
  591. if(!!io_texts.body === true) {
  592. re_body = go_self.myGM.addElement(ls_bodyType, le_center, go_self.myGM.merge({
  593. 'id': 'notificationPanelBodyMContent' + ii_id,
  594. 'class': 'notificationPanelBodyMContent',
  595. 'innerHTML': io_texts.body
  596. }, lo_generalOptions), true);
  597. } else {
  598. go_self.myGM.addElement('div', le_center, { 'id': 'notificationPanelBodyMTop' + ii_id, 'class': 'notificationPanelBodyMTop', 'innerHTML': io_texts.top }, true);
  599. re_body = go_self.myGM.addElement(ls_bodyType, le_center, go_self.myGM.merge({
  600. 'id': 'notificationPanelBodyMBottom' + ii_id,
  601. 'class': 'notificationPanelBodyMBottom',
  602. 'innerHTML': io_texts.bottom
  603. }, lo_generalOptions), true);
  604. }
  605. if(io_options.textarea !== true)
  606. re_body = null;
  607. go_self.myGM.addElement('div', le_center, { 'id': 'notificationPanelBodyPlaceholder' + ii_id, 'class': 'notificationPanelBodyPlaceholder' }, true);
  608. return re_body;
  609. };
  610. /**
  611. * Create the footer for a notification panel.
  612. *
  613. * @private
  614. * @inner
  615. *
  616. * @param {int} ii_id
  617. * The id of the notification.
  618. * @param {Element} ie_panel
  619. * The panel of the notification.
  620. */
  621. var _createNotificationPanelFooter = function(ii_id, ie_panel) {
  622. var le_wrapper = go_self.myGM.addElement('div', ie_panel, { 'id': 'notificationPanelFooter' + ii_id, 'class': 'notificationPanelFooter' }, true);
  623. var le_left = go_self.myGM.addElement('div', le_wrapper, { 'id': 'notificationPanelFooterL' + ii_id, 'class': 'notificationPanelFooterL' }, true);
  624. var le_right = go_self.myGM.addElement('div', le_left, { 'id': 'notificationPanelFooterR' + ii_id, 'class': 'notificationPanelFooterR' }, true);
  625. go_self.myGM.addElement('div', le_right, {
  626. 'id': 'notificationPanelFooterM' + ii_id,
  627. 'class': 'notificationPanelFooterM',
  628. 'innerHTML': go_script.name + ' v' + go_script.version
  629. }, true);
  630. };
  631. /**
  632. * Create the buttons for a notification panel.
  633. *
  634. * @private
  635. * @inner
  636. *
  637. * @param {int} ii_id
  638. * The id of the notification.
  639. * @param {Element} ie_panel
  640. * The panel of the notification.
  641. * @param {Element} ie_body
  642. * The body of the notification.
  643. * @param {IkariamCore~myGM~NotificationButtonsText} io_texts
  644. * The texts for the buttons.
  645. * @param {IkariamCore~myGM~NotificationButtonCallbacks} io_callbacks
  646. * The callbacks for the buttons.
  647. */
  648. var _createNotificationPanelButtons = function(ii_id, ie_panel, ie_body, io_texts, io_callbacks) {
  649. var le_wrapper = go_self.myGM.addElement('div', ie_panel, {
  650. 'id': 'notificationPanelButtonWrapper' + ii_id,
  651. 'class': 'notificationPanelButtonWrapper'
  652. }, true);
  653. var lf_confirm;
  654. if(!!io_callbacks.confirm === true)
  655. lf_confirm = function() { io_callbacks.close(); io_callbacks.confirm(ie_body); };
  656. else
  657. lf_confirm = io_callbacks.close;
  658. go_self.myGM.addElement('input', le_wrapper, {
  659. 'id': 'notificationPanelConfirm' + ii_id,
  660. 'classes': ['notificationPanelButton', 'notificationPanelButtonConfirm'],
  661. 'type': 'button',
  662. 'value': io_texts.confirm ? io_texts.confirm : go_self.Language.$('core.notification.button.confirm'),
  663. 'click': lf_confirm
  664. }, true);
  665. if(!!io_callbacks.abort === true) {
  666. go_self.myGM.addElement('input', le_wrapper, {
  667. 'id': 'notificationPanelAbort' + ii_id,
  668. 'classes': ['notificationPanelButton', 'notificationPanelButtonAbort'],
  669. 'type': 'button',
  670. 'value': io_texts.abort ? io_texts.abort : go_self.Language.$('core.notification.button.abort'),
  671. 'click': function() { io_callbacks.close(); io_callbacks.abort(ie_body); }
  672. }, true);
  673. }
  674. };
  675. /*-------------------------------------------*
  676. * Public variables, functions and settings. *
  677. *-------------------------------------------*/
  678. /**
  679. * Script identifying prefix.
  680. *
  681. * @instance
  682. * @readonly
  683. * @name prefix
  684. * @memberof IkariamCore~myGM
  685. *
  686. * @type {String}
  687. */
  688. Object.defineProperty(this, 'prefix', { get: function() {
  689. return 'script' + go_script.id;
  690. } });
  691. /**
  692. * Returns if the script is already executed on this page.
  693. *
  694. * @instance
  695. * @readonly
  696. * @name alreadyExecuted
  697. * @memberof IkariamCore~myGM
  698. *
  699. * @type {boolean}
  700. */
  701. Object.defineProperty(this, 'alreadyExecuted', { get: function() {
  702. if(this.$('#' + this.prefix + 'alreadyExecuted'))
  703. return true;
  704. // Add the hint, that the script was already executed.
  705. this.addElement('input', this.$('body'), { 'id': 'alreadyExecuted', 'type': 'hidden' });
  706. return false;
  707. } });
  708. /**
  709. * Store a value specified by a key.
  710. *
  711. * @instance
  712. *
  713. * @param {String} is_key
  714. * The key of the value.
  715. * @param {*} im_value
  716. * The value to store.
  717. *
  718. * @return {Promise}
  719. * A promise which resolves once the value is stored.
  720. */
  721. this.setValue = function(is_key, im_value) {
  722. // Stringify the value to store also arrays.
  723. var ls_toStore = JSON.stringify(im_value);
  724. var ro_promise = null;
  725. // If the use of GM_setValue is possible, use it.
  726. if(_gb_canUseGmStorage) {
  727. GM_setValue(is_key, ls_toStore);
  728. // If the use of GM.setValue is possible, use it.
  729. } else if(_gb_canUseGmStorageNew) {
  730. ro_promise = GM.setValue(is_key, ls_toStore);
  731. // Otherwise use the local storage if possible.
  732. } else if(_gb_canUseLocalStorage) {
  733. go_self.win.localStorage.setItem(this.prefix + is_key, ls_toStore);
  734. // Otherwise use cookies.
  735. } else {
  736. var ls_data = escape(this.prefix + is_key) + '=' + escape(ls_toStore);
  737. var ls_expire = 'expires=' + (new Date(2020, 0, 1, 0, 0, 0, 0)).toGMTString();
  738. var ls_path = 'path=/';
  739. var ls_domain = 'domain=' + _gs_cookieDomain;
  740. go_self.win.document.cookie = ls_data + ';' + ls_expire + ';' + ls_path + ';' + ls_domain;
  741. }
  742. if(ro_promise == null) {
  743. ro_promise = Promise.resolve();
  744. }
  745. // Return the promise.
  746. return ro_promise;
  747. };
  748. /**
  749. * Get a value and return it.
  750. *
  751. * @instance
  752. *
  753. * @param {String} is_key
  754. * The key of the value.
  755. * @param {*} im_defaultValue
  756. * The value which is set if the value is not set.
  757. *
  758. * @return {Promise}
  759. * A Promise which resolves to the stored value.
  760. */
  761. this.getValue = function(is_key, im_defaultValue) {
  762. // Put the default value to JSON.
  763. var ls_value = JSON.stringify(im_defaultValue);
  764. var lo_promise = null;
  765. // If the use of GM_getValue is possible, use it.
  766. if(_gb_canUseGmStorage) {
  767. ls_value = GM_getValue(is_key, ls_value);
  768. // If the use of GM.getValue is possible, use it.
  769. } else if(_gb_canUseGmStorageNew) {
  770. lo_promise = GM.getValue(is_key, ls_value);
  771. // Otherwise use the local storage if possible.
  772. } else if(_gb_canUseLocalStorage) {
  773. var ls_storageValue = go_self.win.localStorage.getItem(this.prefix + is_key);
  774. if(ls_storageValue) {
  775. ls_value = ls_storageValue;
  776. }
  777. // Otherwise use cookies.
  778. } else {
  779. var la_allCookies = document.cookie.split("; ");
  780. for(var i = 0; i < la_allCookies.length; i++) {
  781. var la_oneCookie = la_allCookies[i].split("=");
  782. if(la_oneCookie[0] == escape(this.prefix + is_key)) {
  783. ls_value = unescape(la_oneCookie[1]);
  784. break;
  785. }
  786. }
  787. }
  788. if(lo_promise == null) {
  789. lo_promise = Promise.resolve(ls_value);
  790. }
  791. // Return the value (parsed for the correct return type).
  792. return lo_promise.then(function(is_stringifiedValue) { return JSON.parse(is_stringifiedValue); });
  793. };
  794. /**
  795. * Delete a value specified by a key.
  796. *
  797. * @instance
  798. *
  799. * @param {String} is_key
  800. * The key of the value.
  801. *
  802. * @return {Promise}
  803. * A promise which resolves once the value is deleted.
  804. */
  805. this.deleteValue = function(is_key) {
  806. var ro_promise = null;
  807. // If the use of GM_deleteValue is possible, use it.
  808. if(_gb_canUseGmStorage) {
  809. GM_deleteValue(is_key);
  810. // If the use of GM.deleteValue is possible, use it.
  811. } else if(_gb_canUseGmStorageNew) {
  812. ro_promise = GM.deleteValue(is_key);
  813. // Otherwise use the local storage if possible.
  814. } else if(_gb_canUseLocalStorage) {
  815. go_self.win.localStorage.removeItem(this.prefix + is_key);
  816. // Otherwise use cookies.
  817. } else {
  818. var ls_data = escape(this.prefix + is_key) + '=';
  819. var ls_expire = 'expires=' + (new Date(2000, 0, 1, 0, 0, 0, 0)).toGMTString();
  820. var ls_path = 'path=/';
  821. var ls_domain = 'domain=' + _gs_cookieDomain;
  822. go_self.win.document.cookie = ls_data + ';' + ls_expire + ';' + ls_path + ';' + ls_domain;
  823. }
  824. if(ro_promise == null) {
  825. ro_promise = Promise.resolve();
  826. }
  827. // Return the promise.
  828. return ro_promise;
  829. };
  830. /**
  831. * Returns an array with the keys of all values stored by the script.
  832. *
  833. * @instance
  834. *
  835. * @return {Promise}
  836. * A Promise which resolves to the array with all keys.
  837. */
  838. this.listValues = function() {
  839. // Create an array for the storage of the values keys.
  840. var la_key = new Array();
  841. var ro_promise = null;
  842. // If the use of GM_listValues is possible, use it.
  843. if(_gb_canUseGmStorage) {
  844. la_key = GM_listValues();
  845. // If the use of GM.listValues is possible, use it.
  846. } else if(_gb_canUseGmStorageNew) {
  847. ro_promise = GM.listValues();
  848. // Otherwise use the local storage if possible.
  849. } else if(_gb_canUseLocalStorage) {
  850. for(var i = 0; i < go_self.win.localStorage.length; i++) {
  851. var ls_keyName = go_self.win.localStorage.key(i);
  852. if(ls_keyName.indexOf(this.prefix) != -1) {
  853. la_key.push(ls_keyName.replace(this.prefix, ''));
  854. }
  855. }
  856. // Otherwise use cookies.
  857. } else {
  858. var la_allCookies = document.cookie.split("; ");
  859. for(var i = 0; i < la_allCookies.length; i++) {
  860. var ls_keyName = unescape(la_allCookies[i].split("=")[0]);
  861. if(ls_keyName.indexOf(this.prefix) != -1) {
  862. la_key.push(ls_keyName.replace(this.prefix, ''));
  863. }
  864. }
  865. }
  866.  
  867. if(ro_promise == null) {
  868. ro_promise = Promise.resolve(la_key);
  869. }
  870. // Return the promise.
  871. return ro_promise;
  872. };
  873. /**
  874. * Adds a style element to the head of the page and return it.
  875. *
  876. * @instance
  877. *
  878. * @param {String} is_styleRules
  879. * The style rules to be set.
  880. * @param {?String} [is_id=stylesheet not stored]
  881. * An id for the style set, to have the possibility to delete it.
  882. * @param {?boolean} [ib_overwrite=false]
  883. * If a style with id should overwrite an existing style.
  884. *
  885. * @return {boolean}
  886. * If the stylesheet was stored with the id.
  887. */
  888. this.addStyle = function(is_styleRules, is_id, ib_overwrite) {
  889. var rb_storedWithId = false;
  890. if(ib_overwrite && ib_overwrite == true)
  891. this.removeStyle(is_id);
  892. if(!is_id || (is_id && !_go_styleSheets[is_id])) {
  893. var le_style = this.addElement('style', document.head, { 'type': 'text/css', 'innerHTML': is_styleRules });
  894. if(is_id) {
  895. _go_styleSheets[is_id] = le_style;
  896. rb_storedWithId = true;
  897. }
  898. }
  899. return rb_storedWithId;
  900. };
  901. /**
  902. * Removes a style element set by the script.
  903. *
  904. * @instance
  905. *
  906. * @param {String} is_id
  907. * The id of the stylesheet to delete.
  908. *
  909. * @return {boolean}
  910. * If the stylesheet could be deleted.
  911. */
  912. this.removeStyle = function(is_id) {
  913. var rb_removed = false;
  914. if(is_id && _go_styleSheets[is_id]) {
  915. document.head.removeChild(_go_styleSheets[is_id]);
  916. delete _go_styleSheets[is_id];
  917. rb_removed = true;
  918. }
  919. return rb_removed;
  920. };
  921. /**
  922. * Makes a cross-site XMLHttpRequest.
  923. *
  924. * @instance
  925. *
  926. * @param {Object} io_args
  927. * The arguments the request needs. (specified here: {@link http://wiki.greasespot.net/GM_xmlhttpRequest GM_xmlhttpRequest})
  928. *
  929. * @return {Promise}
  930. * A Promise which resolves to the response text (in case of an synchronous request) or a boolean false / JSON string indicating an error.
  931. */
  932. this.xhr = function(io_args) {
  933. var ro_promise;
  934. // Whether the link fetches json data
  935. var lb_isJSON = (io_args.url.search(/\.json$/i) != -1);
  936. // Check if all required data is given.
  937. if(!io_args.method || !io_args.url || !io_args.onload) {
  938. ro_promise = Promise.resolve(false);
  939. // If the use of GM_xmlhttpRequest is possible, use it.
  940. } else if(_gb_canUseGmXhr) {
  941. var lm_response = GM_xmlhttpRequest(io_args);
  942. ro_promise = Promise.resolve(lm_response.responseText);
  943. // If the use of GM.xmlHttpRequest is possible, use it.
  944. } else if(_gb_canUseGmXhrNew) {
  945. /*
  946. * GM 4.0 does not return a promise for the GM.xmlHttpRequest => synchronous requests will fail, asynchronous requests don't care at all.
  947. */
  948. ro_promise = new Promise(function(resolve, reject) {
  949. var lb_synchronous = !!io_args.synchronous;
  950. delete io_args.synchronous;
  951. var lf_originalOnload = io_args.onload;
  952. io_args.onload = function(im_result) {
  953. // In the synchronous call case: resolve in callback.
  954. if(lb_synchronous === true) {
  955. resolve(im_result);
  956. } else {
  957. lf_originalOnload(im_result);
  958. }
  959. };
  960.  
  961. var lo_responseObject = GM.xmlHttpRequest(io_args);
  962. // In the asynchronous call case: resolve directly.
  963. if(lb_synchronous === false) {
  964. resolve(lo_responseObject);
  965. }
  966. }).then(function(io_response) {
  967. return io_response && io_response.responseText ? io_response.responseText : '';
  968. });
  969.  
  970. // Otherwise show a hint for the missing possibility to fetch the data.
  971. } else {
  972. // Otherwise if it is JSON.
  973. if(lb_isJSON) {
  974. io_args.onload('{ "is_error": true }');
  975. ro_promise = Promise.resolve('{ "is_error": true }');
  976. // Otherwise.
  977. } else {
  978. ro_promise = Promise.resolve(false);
  979. }
  980. }
  981. // Return the responseText.
  982. return ro_promise;
  983. };
  984. /**
  985. * Returns the content of a resource parsed with JSON.parse.
  986. *
  987. * @instance
  988. *
  989. * @param {String} is_name
  990. * The name of the resource to parse.
  991. * @param {String} is_xhrUrl
  992. * The resource to fetch the resource file from if the use of <code>GM_getResourceText</code> is not possible.
  993. *
  994. * @return {Object}
  995. * A Promise which resolves to the parsed resource.
  996. */
  997. this.getResourceParsed = function(is_name, is_xhrUrl) {
  998. var lo_promise;
  999. // Function for safer parsing.
  1000. var lf_safeParse = function(is_key, im_value) {
  1001. // If the value is a function, return just the string, so it is not executable.
  1002. if(typeof im_value === 'function' || Object.prototype.toString.apply(im_value) === '[object function]') {
  1003. return im_value.toString();
  1004. }
  1005.  
  1006. return im_value;
  1007. };
  1008. // If the use of the default GM_getRessourceText is possible, use it.
  1009. if(_gb_canUseGmRessource) {
  1010. lo_promise = Promise.resolve(GM_getResourceText(is_name));
  1011. // Otherwise perform a xmlHttpRequest (and cache the response).
  1012. } else {
  1013. var ls_key = 'core_cachedResource_' + is_name;
  1014. lo_promise = go_self.myGM.getValue(ls_key, null).then(function(io_cached) {
  1015. if(io_cached && io_cached.forScriptVersion == go_script.version) {
  1016. return io_cached.resourceContent;
  1017. }
  1018. return go_self.myGM.xhr({
  1019. method: 'GET',
  1020. url: is_xhrUrl,
  1021. headers: { 'User-agent': navigator.userAgent, 'Accept': 'text/html' },
  1022. synchronous: true,
  1023. onload: function(im_response) { return false; }
  1024. }).then(function(is_responseText) {
  1025. // Cache the response.
  1026. go_self.myGM.setValue(ls_key, {
  1027. forScriptVersion: go_script.version,
  1028. resourceContent: is_responseText,
  1029. });
  1030. return is_responseText;
  1031. });
  1032. });
  1033. }
  1034. return lo_promise.then(function(is_responseText) { return JSON.parse(is_responseText, lf_safeParse); });
  1035. };
  1036. /**
  1037. * Gets the first matching child element by a (css) query and returns it.
  1038. *
  1039. * @instance
  1040. *
  1041. * @param {String} is_query
  1042. * The query for the element.
  1043. * @param {?Element} [ie_parent=document]
  1044. * The parent element.
  1045. *
  1046. * @return {Element}
  1047. * The element.
  1048. */
  1049. this.$ = function(is_query, ie_parent) {
  1050. return this.$$(is_query, ie_parent)[0];
  1051. };
  1052. /**
  1053. * Gets all matching child elements by a (css) query and returns them.
  1054. *
  1055. * @instance
  1056. *
  1057. * @param {String} is_query
  1058. * The query for the elements.
  1059. * @param {?Element} [ie_parent=document]
  1060. * The parent element.
  1061. *
  1062. * @return {Array.<Element>}
  1063. * The elements.
  1064. */
  1065. this.$$ = function(is_query, ie_parent) {
  1066. var le_parent = ie_parent || document;
  1067. // Return the elements as array, not as element list.
  1068. return Array.prototype.slice.call(le_parent.querySelectorAll(is_query));
  1069. };
  1070. /**
  1071. * Returns the value of the selected option of a select field.
  1072. *
  1073. * @param {String} is_id
  1074. * The last part of the id of the element.
  1075. * @param {?boolean} [ib_hasNoPrefix=false]
  1076. * If the id has no prefix.
  1077. * @param {?boolean} [ib_addNoSelect=false]
  1078. * If there should be no "Select" at the end of the id.
  1079. *
  1080. * @return {String}
  1081. * The value.
  1082. */
  1083. this.getSelectValue = function(is_id, ib_hasNoPrefix, ib_addNoSelect) {
  1084. var le_select = this.$('#' + (ib_hasNoPrefix ? '' : this.prefix) + is_id + (ib_addNoSelect ? '' : 'Select'));
  1085. return le_select.options[le_select.selectedIndex].value;
  1086. };
  1087. /**
  1088. * Returns the value of the selected radio button of a radio button group.
  1089. *
  1090. * @param {String} is_name
  1091. * The last part of the name of the element.
  1092. * @param {?boolean} [ib_hasNoPrefix=false]
  1093. * If the name has no prefix.
  1094. *
  1095. * @return {String}
  1096. * The value.
  1097. */
  1098. this.getRadioValue = function(is_name, ib_hasNoPrefix) {
  1099. var le_radios = this.$$('input[name="' + (ib_hasNoPrefix ? '' : this.prefix) + is_name + '"]');
  1100. var rs_value = '';
  1101. for(var i = 0; i < le_radios.length; i++) {
  1102. if(le_radios[i].checked) {
  1103. rs_value = le_radios[i].value;
  1104. break;
  1105. }
  1106. }
  1107. return rs_value;
  1108. };
  1109. /**
  1110. * Creates a new element and adds it to a parent.
  1111. *
  1112. * @instance
  1113. *
  1114. * @param {String} is_type
  1115. * The type of the new element.
  1116. * @param {Element} ie_parent
  1117. * The parent of the new element.
  1118. * @param {?IkariamCore~myGM~NewElementOptions} [io_options]
  1119. * Options for the new element like id, class(es), style, type etc.
  1120. * @param {?(boolean|IkariamCore~myGM~HasPrefix)} [im_hasPrefix={id: true, classes: false}]
  1121. * If a prefix should be used.
  1122. * @param {?Element} [ie_nextSibling=end of parent]
  1123. * The next sibling of the element.
  1124. *
  1125. * @return {Element}
  1126. * The new element.
  1127. */
  1128. this.addElement = function(is_type, ie_parent, io_options, im_hasPrefix, ie_nextSibling) {
  1129. var re_newElement = document.createElement(is_type);
  1130. if(!!io_options === true) {
  1131. this.forEach(io_options, function(is_key, im_property) {
  1132. var ls_prefix = '';
  1133. if('id' === is_key && !(im_hasPrefix === false || (im_hasPrefix && im_hasPrefix.id === false))) {
  1134. ls_prefix = go_self.myGM.prefix;
  1135. }
  1136. if('class' === is_key) {
  1137. if(im_hasPrefix === true || (im_hasPrefix && im_hasPrefix.classes === true))
  1138. ls_prefix = go_self.myGM.prefix;
  1139. if(im_property !== '')
  1140. re_newElement.classList.add(ls_prefix + im_property);
  1141. return;
  1142. }
  1143. if('classes' === is_key) {
  1144. if(im_hasPrefix === true || (im_hasPrefix && im_hasPrefix.classes === true))
  1145. ls_prefix = go_self.myGM.prefix;
  1146. for(var i = 0; i < im_property.length; i++) {
  1147. if(im_property[i] != '')
  1148. re_newElement.classList.add(ls_prefix + im_property[i]);
  1149. }
  1150. return;
  1151. }
  1152. if('style' === is_key) {
  1153. for(var i = 0; i < im_property.length; i++) {
  1154. re_newElement.style[im_property[i][0]] = im_property[i][1];
  1155. }
  1156. return;
  1157. }
  1158. if('click' === is_key || 'focus' === is_key) {
  1159. re_newElement.addEventListener(is_key, im_property, false);
  1160. return;
  1161. }
  1162. if('innerHTML' === is_key) {
  1163. re_newElement[is_key] = im_property;
  1164. return;
  1165. }
  1166. re_newElement.setAttribute(is_key, ls_prefix + im_property);
  1167. });
  1168. }
  1169. ie_parent.insertBefore(re_newElement, ie_nextSibling);
  1170. return re_newElement;
  1171. };
  1172. /**
  1173. * Removes an element from its parent.
  1174. *
  1175. * @instance
  1176. *
  1177. * @param {(Element|Array.<Element>)} im_toRemove
  1178. * The element to remove.
  1179. */
  1180. this.removeElement = function(im_toRemove) {
  1181. if(!!im_toRemove === false)
  1182. return;
  1183. var la_toRemove = im_toRemove;
  1184. if(Array.isArray(im_toRemove) === false)
  1185. la_toRemove = [im_toRemove];
  1186. for(var i = 0; i < la_toRemove.length; i++) {
  1187. la_toRemove[i].parentNode.removeChild(la_toRemove[i]);
  1188. }
  1189. };
  1190. /**
  1191. * Creates new checkboxes and adds it to a parent.
  1192. *
  1193. * @instance
  1194. *
  1195. * @param {Element} ie_parent
  1196. * The parent of the new checkboxes.
  1197. * @param {Array.<IkariamCore~myGM~NewCheckboxData>} ia_cbData
  1198. * The data of the checkboxes.
  1199. */
  1200. this.addCheckboxes = function(ie_parent, ia_cbData) {
  1201. for(var i = 0; i < ia_cbData.length; i++) {
  1202. var le_wrapper = this.addElement('div', ie_parent, { 'class': 'cbWrapper' });
  1203. var ls_label = typeof ia_cbData[i].label == 'string' ? ia_cbData[i].label : go_self.Language.$(ia_cbData[i].label.id);
  1204. var la_options = {
  1205. 'id': ia_cbData[i]['id'] + 'Cb',
  1206. 'class': 'checkbox',
  1207. 'type': 'checkbox',
  1208. 'title': ls_label
  1209. };
  1210. if(!!ia_cbData[i]['checked'] === true)
  1211. la_options['checked'] = 'checked';
  1212. this.addElement('input', le_wrapper, la_options);
  1213. }
  1214. };
  1215. /**
  1216. * Creates a new radio button group and adds it to a parent table.
  1217. *
  1218. * @instance
  1219. *
  1220. * @param {Element} ie_parentTable
  1221. * The parent table of the new select field.
  1222. * @param {String} is_name
  1223. * The last part of the name of the radio button group.
  1224. * @param {(String|int)} im_checked
  1225. * The value of the selected option.
  1226. * @param {Array.<IkariamCore~myGM~ValueAndLabel>} ia_options
  1227. * An array with the names an values of the options.
  1228. * @param {String} is_labelText
  1229. * The text of the select label.
  1230. */
  1231. this.addRadios = function(ie_parentTable, is_name, im_checked, ia_options, is_labelText) {
  1232. var le_row = this.addElement('tr', ie_parentTable);
  1233. var le_labelCell = this.addElement('td', le_row, { 'class': 'vertical_top' });
  1234. var le_radioCell = this.addElement('td', le_row, { 'class': 'left' });
  1235. var ls_labelText = typeof is_labelText == 'string' ? is_labelText : go_self.Language.$(is_labelText.id);
  1236. this.addElement('span', le_labelCell, { 'innerHTML': ls_labelText });
  1237. for(var i = 0; i < ia_options.length; i++) {
  1238. var le_wrapper = this.addElement('div', le_radioCell, { 'class': 'radioWrapper' });
  1239. var ls_optionLabel = typeof ia_options[i].label == 'string' ? ia_options[i].label : go_self.Language.$(ia_options[i].label.id);
  1240. this.addElement('input', le_wrapper, {
  1241. 'class': 'checkbox',
  1242. 'type': 'radio',
  1243. 'name': this.prefix + is_name,
  1244. 'value': ia_options[i].value,
  1245. 'title': ls_optionLabel,
  1246. 'checked': ia_options[i].value == im_checked ? 'checked' : ''
  1247. });
  1248. }
  1249. };
  1250. /**
  1251. * Creates a new select field and adds it to a parent table.
  1252. *
  1253. * @instance
  1254. *
  1255. * @param {Element} ie_parentTable
  1256. * The parent table of the new select field.
  1257. * @param {String} is_id
  1258. * The last part of the id of the select field.
  1259. * @param {(String|int)} im_selected
  1260. * The value of the selected option.
  1261. * @param {Array.<IkariamCore~myGM~ValueAndLabel>} ia_options
  1262. * An array with the names an values of the options.
  1263. * @param {String} is_labelText
  1264. * The text of the select label.
  1265. */
  1266. this.addSelect = function(ie_parentTable, is_id, im_selected, ia_options, is_labelText) {
  1267. var le_row = this.addElement('tr', ie_parentTable);
  1268. var le_labelCell = this.addElement('td', le_row);
  1269. var le_selectCell = this.addElement('td', le_row, { 'class': 'left' });
  1270. var ls_labelText = typeof is_labelText == 'string' ? is_labelText : go_self.Language.$(is_labelText.id);
  1271. this.addElement('span', le_labelCell, { 'innerHTML': ls_labelText });
  1272. var le_wrapper = this.addElement('div', le_selectCell, {
  1273. 'id': is_id + 'SelectContainer',
  1274. 'classes': ['select_container', 'size175'],
  1275. 'style': [['position', 'relative']]
  1276. });
  1277. var le_select = this.addElement('select', le_wrapper, { 'id': is_id + 'Select', 'class': 'dropdown' });
  1278. for(var i = 0; i < ia_options.length; i++) {
  1279. var ls_optionLabel = typeof ia_options[i].label == 'string' ? ia_options[i].label : go_self.Language.$(ia_options[i].label.id);
  1280. var le_option = this.addElement('option', le_select, { 'value': ia_options[i].value, 'innerHTML': ls_optionLabel });
  1281. if(le_option.value == im_selected) {
  1282. le_option.selected = 'selected';
  1283. }
  1284. }
  1285. };
  1286. /**
  1287. * Creates a button and adds it to a parent.
  1288. *
  1289. * @instance
  1290. *
  1291. * @param {Element} ie_parent
  1292. * The parent element.
  1293. * @param {String} is_value
  1294. * The value of the button.
  1295. * @param {function} if_callback
  1296. * A callback which should be called when the user clicks on the button.
  1297. * @param {boolean} [ib_parentIsWrapper=false]
  1298. * If the element provided as parent is also the button wrapper.
  1299. */
  1300. this.addButton = function(ie_parent, is_value, if_callback, ib_parentIsWrapper) {
  1301. var le_buttonWrapper = ie_parent;
  1302. if(ib_parentIsWrapper !== true)
  1303. le_buttonWrapper = this.addElement('div', ie_parent, { 'class': 'centerButton' });
  1304.  
  1305. var ls_value = typeof is_value == 'string' ? is_value : go_self.Language.$(is_value.id);
  1306. var re_button = this.addElement('input', le_buttonWrapper, {
  1307. 'class': 'button',
  1308. 'type': 'button',
  1309. 'value': ls_value,
  1310. 'click': if_callback
  1311. });
  1312. return re_button;
  1313. };
  1314. /**
  1315. * Shows a notification to the user. You can either create a notification field or an input / output field. If the
  1316. * field should be an input field, the field is given to the callbacks as parameter. The abort button is only shown
  1317. * if the abort callback is set. It is also possible to have two body parts or just one body part. This functionality
  1318. * is set by the notification text.
  1319. *
  1320. * @instance
  1321. *
  1322. * @param {IkariamCore~myGM~NotificationText} im_text
  1323. * The notification texts.
  1324. * @param {?IkariamCore~myGM~NotificationCallbacks} [im_callback]
  1325. * The callbacks for confirm and abort.
  1326. * @param {IkariamCore~myGM~NotificationBodyOptions} [io_options]
  1327. * Options for the body.
  1328. *
  1329. * @return {int}
  1330. * The notification id.
  1331. */
  1332. this.notification = function(im_text, im_callback, io_options) {
  1333. _gi_notificationId++;
  1334. var lo_options = io_options || {};
  1335. // Set a local notification id to be able to have more than 1 notification panels.
  1336. var ri_notificationId = _gi_notificationId;
  1337. // Function to close the notification panel.
  1338. var lf_closePanel = function() {
  1339. go_self.myGM.removeElement([
  1340. go_self.myGM.$('#' + go_self.myGM.prefix + 'notificationBackground' + ri_notificationId),
  1341. go_self.myGM.$('#' + go_self.myGM.prefix + 'notificationPanelContainer' + ri_notificationId)
  1342. ]);
  1343. };
  1344. // Create the background and the container.
  1345. this.addElement('div', document.body, { 'id': 'notificationBackground' + ri_notificationId, 'class': 'notificationBackground' }, true);
  1346. var le_panelContainer = this.addElement('div', document.body, { 'id': 'notificationPanelContainer' + ri_notificationId, 'class': 'notificationPanelContainer' }, true);
  1347. var le_panel = this.addElement('div', le_panelContainer, { 'id': 'notificationPanel' + ri_notificationId, 'class': 'notificationPanel' }, true);
  1348. // Create the notification panel header.
  1349. var ls_headerText = im_text.header ? im_text.header : go_self.Language.$('core.notification.header');
  1350. _createNotificationPanelHeader(ri_notificationId, le_panel, ls_headerText, lf_closePanel);
  1351. // Create the notification panel body.
  1352. var lo_bodyTexts = {
  1353. body: im_text.body,
  1354. top: im_text.bodyTop ? im_text.bodyTop : '',
  1355. bottom: im_text.bodyBottom ? im_text.bodyBottom : ''
  1356. };
  1357. var le_body = _createNotificationPanelBody(ri_notificationId, le_panel, lo_options, lo_bodyTexts);
  1358. // Create the notification panel footer.
  1359. _createNotificationPanelFooter(ri_notificationId, le_panel);
  1360. // Create the buttons.
  1361. var lo_buttonTexts = {
  1362. confirm: im_text.confirm ? im_text.confirm : null,
  1363. abort: im_text.abort ? im_text.abort : null
  1364. };
  1365. var lo_buttonCallbacks = {
  1366. close: lf_closePanel,
  1367. confirm: im_callback && im_callback.confirm ? im_callback.confirm : null,
  1368. abort: im_callback && im_callback.abort ? im_callback.abort : null
  1369. };
  1370. _createNotificationPanelButtons(ri_notificationId, le_panel, le_body, lo_buttonTexts, lo_buttonCallbacks);
  1371. return ri_notificationId;
  1372. };
  1373. /**
  1374. * Toogle the show / hide Button image and title.
  1375. *
  1376. * @instance
  1377. *
  1378. * @param {Element} ie_button
  1379. * The button to toggle.
  1380. */
  1381. this.toggleShowHideButton = function(ie_button) {
  1382. ie_button.classList.toggle('minimizeImg');
  1383. ie_button.classList.toggle('maximizeImg');
  1384. ie_button.title = (ie_button.title == go_self.Language.$('general.fold')) ? go_self.Language.$('general.expand') : go_self.Language.$('general.fold');
  1385. };
  1386. /**
  1387. * Runs a callback on every property of an object which is not in the prototype.
  1388. *
  1389. * @instance
  1390. *
  1391. * @param {Object} io_object
  1392. * The Object where forEach should be used.
  1393. * @param {IkariamCore~myGM~ForEachCallback} if_callback
  1394. * The callback which should be called.
  1395. */
  1396. this.forEach = function(io_object, if_callback) {
  1397. for(var ls_key in io_object) {
  1398. if(Object.prototype.hasOwnProperty.call(io_object, ls_key)) {
  1399. if_callback(ls_key, io_object[ls_key]);
  1400. }
  1401. }
  1402. };
  1403. /**
  1404. * Merges objects.
  1405. *
  1406. * @instance
  1407. *
  1408. * @param {...Object} arguments
  1409. * All objects to merge into each other.
  1410. *
  1411. * @return {Object}
  1412. * The merged object.
  1413. */
  1414. this.merge = function() {
  1415. var ro_merged = {};
  1416. for(var i = 0; i < arguments.length; i++) {
  1417. go_self.myGM.forEach(arguments[i], function(is_key, im_value) {
  1418. if(typeof ro_merged[is_key] === 'object' && typeof im_value === 'object')
  1419. go_self.myGM.merge(ro_merged[is_key], im_value);
  1420. else
  1421. ro_merged[is_key] = im_value;
  1422. });
  1423. }
  1424. return ro_merged;
  1425. };
  1426. /*--------------------*
  1427. * Set some settings. *
  1428. *--------------------*/
  1429. // Set the notification style.
  1430. this.addStyle(
  1431. "." + 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; } \
  1432. ." + 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: #542C0F; font: 12px Arial,Helvetica,sans-serif; } \
  1433. ." + this.prefix + "notificationPanel { position: relative; top: 0px; left: 0px; background-color: transparent; border: 0 none; overflow: hidden; } \
  1434. ." + this.prefix + "notificationPanelHeader { height: 39px; background: none repeat scroll 0 0 transparent; font-weight: bold; line-height: 2; white-space: nowrap; } \
  1435. ." + this.prefix + "notificationPanelHeaderL { height: 39px; background-image: url('skin/layout/notes_top_left.png'); background-position: left top; background-repeat: no-repeat; } \
  1436. ." + this.prefix + "notificationPanelHeaderR { height: 39px; background-image: url('skin/layout/notes_top_right.png'); background-position: right top; background-repeat: no-repeat; } \
  1437. ." + this.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; } \
  1438. ." + this.prefix + "notificationPanelBody { max-height: 311px; height: 100%; background: none repeat scroll 0 0 transparent; } \
  1439. ." + this.prefix + "notificationPanelBodyL { height: 100%; background-image: url('skin/layout/notes_left.png'); background-position: left top; background-repeat: repeat-y; } \
  1440. ." + this.prefix + "notificationPanelBodyR { height: 100%; background-image: url('skin/layout/notes_right.png'); background-position: right top; background-repeat: repeat-y; } \
  1441. ." + this.prefix + "notificationPanelBodyM { height: 100%; background-color: #F7E7C5; background-image: none; margin: 0 6px; padding: 0 10px; font-size: 14px; } \
  1442. ." + this.prefix + "notificationPanelBodyMTop { max-height: 100px; line-height: 2; } \
  1443. ." + this.prefix + "notificationPanelBodyMTop b { line-height: 3.5; font-size:110%; } \
  1444. ." + this.prefix + "notificationPanelBodyM a { color: #811709; font-weight: bold; } \
  1445. ." + this.prefix + "notificationPanelBodyM h2 { font-weight: bold; } \
  1446. ." + this.prefix + "notificationPanelBodyMContent { max-height: 270px; padding: 10px; background-color: #FFF7E1; border: 1px dotted #C0C0C0; font: 14px Arial,Helvetica,sans-serif; color: #000000; border-collapse: separate; overflow-y:auto; } \
  1447. ." + this.prefix + "notificationPanelBodyMBottom { max-height: 170px; padding: 10px; background-color: #FFF7E1; border: 1px dotted #C0C0C0; font: 14px Arial,Helvetica,sans-serif; color: #000000; border-collapse: separate; overflow-y:auto; } \
  1448. textarea." + this.prefix + "notificationPanelBodyMContent { height: 270px; width: 445px; resize: none; } \
  1449. textarea." + this.prefix + "notificationPanelBodyMBottom { height: 170px; width: 445px; resize: none; } \
  1450. ." + this.prefix + "notificationPanelBodyPlaceholder { height: 20px; } \
  1451. ." + this.prefix + "notificationPanelFooter { height: 20px; background: none repeat scroll 0 0 transparent; } \
  1452. ." + this.prefix + "notificationPanelFooterL { height: 100%; background-image: url('skin/layout/notes_left.png'); background-position: left top; background-repeat: repeat-y; border: 0 none; } \
  1453. ." + this.prefix + "notificationPanelFooterR { height: 21px; background-image: url('skin/layout/notes_br.png'); background-position: right bottom; background-repeat: no-repeat; } \
  1454. ." + this.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%; } \
  1455. ." + this.prefix + "notificationPanelClose { cursor: pointer; position: absolute; top: 12px; right: 8px; width: 17px; height: 17px; background-image: url('skin/layout/notes_close.png'); } \
  1456. ." + this.prefix + "notificationPanelButtonWrapper { bottom: -4px; position: absolute; margin: 10px auto; width: 100%; text-align: center; } \
  1457. ." + this.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; } \
  1458. ." + this.prefix + "notificationPanelButton:hover { color: #B3713F; } \
  1459. ." + this.prefix + "notificationPanelButton:active { border-color: #5D4C2F #C9A584 #C9A584 #5D4C2F; border-style: double; border-width: 3px; padding: 3px 10px 1px; } \
  1460. ." + this.prefix + "notificationPanelButtonConfirm { } \
  1461. ." + this.prefix + "notificationPanelButtonAbort { }",
  1462. 'notification', true
  1463. );
  1464. // Add the buttons for toggle buttons used styles.
  1465. this.addStyle(
  1466. ".minimizeImg, .maximizeImg { background: url('skin/interface/window_control_sprite.png') no-repeat scroll 0 0 transparent; cursor: pointer; display: block; height: 18px; width: 18px; } \
  1467. .minimizeImg { background-position: -144px 0; } \
  1468. .minimizeImg:hover { background-position: -144px -19px; } \
  1469. .maximizeImg { background-position: -126px 0; } \
  1470. .maximizeImg:hover { background-position: -126px -19px; }",
  1471. 'toggleShowHideButton', true
  1472. );
  1473. // Fixe the tab scroll to prevent the scroll left / right button to have a widht more than 40px.
  1474. this.addStyle(
  1475. "#container .tabmenu .tab { width: unset; } \
  1476. #container .tabmenu .tab.tabPrevPage, #container .tabmenu .tab.tabNextPage { width: 40px; }",
  1477. 'fixTabScroll', true
  1478. );
  1479. /*---------------------------------------------------------------------*
  1480. * Types for documentation purposes (e.g. callback functions, objects) *
  1481. *---------------------------------------------------------------------*/
  1482. /**
  1483. * Confirm / abort callback for a notification with an input text field.
  1484. *
  1485. * @callback IkariamCore~myGM~ConfirmAbortWithInput
  1486. *
  1487. * @param {Element} textarea
  1488. * The textarea element which contains the user input.
  1489. */
  1490. /**
  1491. * Confirm / abort callback for a notification without an input text field.
  1492. *
  1493. * @callback IkariamCore~myGM~ConfirmAbortWithoutInput
  1494. */
  1495. /**
  1496. * Callbacks to confirm / abort a notification.
  1497. *
  1498. * @typedef IkariamCore~myGM~NotificationCallbacks
  1499. *
  1500. * @property {?(IkariamCore~myGM~ConfirmAbortWithInput|IkariamCore~myGM~ConfirmAbortWithoutInput)} [confirm=close panel] - The callback for the confirm button.
  1501. * @property {?(IkariamCore~myGM~ConfirmAbortWithInput|IkariamCore~myGM~ConfirmAbortWithoutInput)} [abort=close panel] - The callback for the abort button.
  1502. */
  1503. /**
  1504. * Callbacks for the buttons of the notification panel.
  1505. *
  1506. * @typedef IkariamCore~myGM~NotificationButtonCallbacks
  1507. *
  1508. * @private
  1509. * @inner
  1510. *
  1511. * @mixes IkariamCore~myGM~NotificationCallbacks
  1512. *
  1513. * @property {IkariamCore~myGM~ConfirmAbortWithoutInput} close - The callback to close the panel.
  1514. */
  1515. /**
  1516. * Options for the notification body.
  1517. *
  1518. * @typedef {Object} IkariamCore~myGM~NotificationBodyOptions
  1519. *
  1520. * @property {boolean} [textarea=false] - If the body should be a textarea.
  1521. * @property {boolean} [readonly=false] - If the textarea is readonly. Only used if textarea=true.
  1522. * @property {boolean} [autofocus=false] - If the textarea content is autoselected on click. Only used if textarea=true.
  1523. */
  1524. /**
  1525. * Text for the notification body. Either body or top AND bottom must be specified.
  1526. *
  1527. * @typedef {Object} IkariamCore~myGM~NotificationBodyText
  1528. *
  1529. * @property {?String} [body] - Text if there is only one text in the body.
  1530. * @property {?String} [top] - Upper text if the body is splitted.
  1531. * @property {?String} [bottom] - Lower text if the body is splitted.
  1532. */
  1533. /**
  1534. * Text for the notification panel buttons.
  1535. *
  1536. * @typedef {Object} IkariamCore~myGM~NotificationButtonsText
  1537. *
  1538. * @property {?String} [confirm=core.notification.button.confirm] - Text for the confirm button.
  1539. * @property {?String} [abort=core.notification.button.abort] - Text for the abort button.
  1540. */
  1541. /**
  1542. * Texts for the notification panel.
  1543. *
  1544. * @typedef IkariamCore~myGM~NotificationText
  1545. *
  1546. * @mixes IkariamCore~myGM~NotificationBodyText
  1547. * @mixes IkariamCore~myGM~NotificationButtonsText
  1548. *
  1549. * @property {?String} [header=core.notification.header] - The notification panel header.
  1550. */
  1551. /**
  1552. * CSS Styles for an element.<br>
  1553. * Structure of the array: <code>[ [ &lt;styleName&gt;, &lt;styleValue&gt; ] ]</code>
  1554. *
  1555. * @typedef {Array.<Array.<String>>} IkariamCore~myGM~CssStyles
  1556. */
  1557. /**
  1558. * Options for a new element.
  1559. *
  1560. * @typedef {Object} IkariamCore~myGM~NewElementOptions
  1561. *
  1562. * @property {String} [id] - The id of the element.
  1563. * @property {String} [class] - A single class of the element.
  1564. * @property {String[]} [classes] - Multiple classes for the element.
  1565. * @property {IkariamCore~myGM~CssStyles} [style] - Styles for the element.
  1566. * @property {function} [click] - An onclick callback.
  1567. * @property {function} [focus] - An onfocus callback.
  1568. * @property {String} [*] - All other element options.
  1569. */
  1570. /**
  1571. * Define if id and classes should have a prefix.
  1572. *
  1573. * @typedef {Object} IkariamCore~myGM~HasPrefix
  1574. *
  1575. * @property {boolean} [id=true] - If the id should have a prefix.
  1576. * @property {boolean} [classes=false] - If the classes should have a prefix.
  1577. */
  1578. /**
  1579. * Data for a new checkbox.
  1580. *
  1581. * @typedef {Object} IkariamCore~myGM~NewCheckboxData
  1582. *
  1583. * @property {String} id - The id of the checkbox.
  1584. * @property {String} label - The label of the checkbox.
  1585. * @property {boolean} checked - If the checkbox is checked.
  1586. */
  1587.  
  1588. /**
  1589. * Data set consisting of value and label.
  1590. *
  1591. * @typedef {Object} IkariamCore~myGM~ValueAndLabel
  1592. *
  1593. * @property {(String|int)} value - The value of the data set.
  1594. * @property {String} label - The label of the data set.
  1595. */
  1596. /**
  1597. * Callback for a forEach iteration on an object.
  1598. *
  1599. * @callback IkariamCore~myGM~ForEachCallback
  1600. *
  1601. * @param {String} propertyKey
  1602. * The key of the property of the object.
  1603. * @param {*} propertyValue
  1604. * The value of the property.
  1605. */
  1606. }
  1607. /**
  1608. * myGM for cross-browser compatibility of the GM_* functions. (use myGM.* instead of GM_*)<br>
  1609. * Also there are general used functions stored.
  1610. *
  1611. * @instance
  1612. *
  1613. * @type IkariamCore~myGM
  1614. */
  1615. this.myGM = new myGM();
  1616. go_self.con.logTimeStamp('IkariamCore.myGM created');
  1617. /**
  1618. * Instantiate a new set of localization functions.
  1619. *
  1620. * @inner
  1621. *
  1622. * @class
  1623. * @classdesc Functions for localizing the script.
  1624. */
  1625. function Language() {
  1626. /*--------------------------------------------*
  1627. * Private variables, functions and settings. *
  1628. *--------------------------------------------*/
  1629. /**
  1630. * Whether the Language module was initialized.
  1631. *
  1632. * @private
  1633. * @inner
  1634. *
  1635. * @type boolean
  1636. */
  1637. _gb_initialized = false;
  1638. /**
  1639. * Mapping for countries where the used language is the same, but the url is different (e.g. us -> USA and en -> Great Britain)
  1640. *
  1641. * @private
  1642. * @inner
  1643. *
  1644. * @type Object.<String, String>
  1645. */
  1646. var _go_codeMapping = {
  1647. ar: 'es',
  1648. br: 'pt',
  1649. mx: 'es',
  1650. us: 'en'
  1651. };
  1652. /**
  1653. * Default Ikariam language code for this server.
  1654. *
  1655. * @private
  1656. * @inner
  1657. *
  1658. * @default en
  1659. *
  1660. * @type String
  1661. */
  1662. var _gs_ikaCode = (function() {
  1663. var rs_uri = top.location.host.match(/^s[0-9]+-([a-zA-Z]+)\.ikariam\.gameforge\.com$/)[1];
  1664. if(!!_go_codeMapping[rs_uri] === true)
  1665. rs_uri = _go_codeMapping[rs_uri];
  1666. if(!rs_uri === true)
  1667. rs_uri = 'en';
  1668. return rs_uri;
  1669. })();
  1670. /**
  1671. * Default language code - code of language registered as default.
  1672. *
  1673. * @private
  1674. * @inner
  1675. *
  1676. * @default en
  1677. *
  1678. * @type String
  1679. */
  1680. var _gs_defaultCode = 'en';
  1681. /**
  1682. * Used language code.
  1683. *
  1684. * @private
  1685. * @inner
  1686. *
  1687. * @default en
  1688. *
  1689. * @type String
  1690. */
  1691. var _gs_usedCode = _gs_defaultCode;
  1692. /**
  1693. * Used language texts. Used if a translation is requested.
  1694. *
  1695. * @private
  1696. * @inner
  1697. *
  1698. * @type json
  1699. */
  1700. var _go_usedText = {};
  1701. /**
  1702. * Default language text. To be used if the used language is not available.
  1703. *
  1704. * @private
  1705. * @inner
  1706. *
  1707. * @type json
  1708. */
  1709. var _go_defaultText = {};
  1710. /**
  1711. * All languages which are registered with their storage type (resource, in-script-object).
  1712. *
  1713. * @private
  1714. * @inner
  1715. *
  1716. * @type Object.<String, Array.<IkariamCore~Language~LanguageSettings>>
  1717. */
  1718. var _go_registeredLangs = {};
  1719. /**
  1720. * "Translation" of all possible language codes to the corresponding language.
  1721. *
  1722. * @TODO Translate when required!
  1723. *
  1724. * @private
  1725. * @inner
  1726. *
  1727. * @type Object.<String, String>
  1728. */
  1729. var _go_codeTranslation = {
  1730. ae: 'Arabic', // ... Arabic
  1731. bg: 'Bulgarian', // ... Bulgarian
  1732. cz: 'Czech', // ... Czech
  1733. de: 'Deutsch', // German
  1734. dk: 'Danish', // ... Danish
  1735. en: 'English', // English
  1736. es: 'Español', // Spanish
  1737. fi: 'Finish', // ... Finish
  1738. fr: 'Français', // French
  1739. gr: 'Ελληνικά', // Greek
  1740. hu: 'Hungarian', // ... Hungarian
  1741. il: 'Hebrew', // ... Hebrew
  1742. it: 'Italiano', // Italian
  1743. lt: 'Lithuanian', // ... Lithuanian
  1744. lv: 'Latviešu', // Latvian
  1745. nl: 'Nederlands', // Dutch
  1746. no: 'Norwegian', // ... Norwegian
  1747. pl: 'Polski', // Polish
  1748. pt: 'Portugese', // ... Portugese
  1749. ro: 'Romanian', // ... Romanian
  1750. rs: 'Serbian', // ... Serbian
  1751. ru: 'Русский', // Russian
  1752. se: 'Svenska', // Swedisch
  1753. si: 'Slovene', // ... Slovene
  1754. sk: 'Slovak', // ... Slovak
  1755. tr: 'Türkçe', // Turkish
  1756. tw: 'Chinese', // ... Chinese
  1757. };
  1758. /**
  1759. * Initialize the core language files.
  1760. *
  1761. * @private
  1762. * @inner
  1763. *
  1764. * @return {Promise}
  1765. * A Promise which resolves once the languages are set.
  1766. */
  1767. var _initialize = async function() {
  1768. var la_language = [
  1769. { code: 'de', settings: {"settings": {"kiloSep":".","decSep":",","ltr":true}}, translations: {"core": {"update": {"notPossible": {"header": "Keine Aktualisierung möglich","text": "Es ist nicht möglich, nach Aktualisierungen für %$1 zu suchen. Bitte prüfe selbst auf Aktualisierungen für das Script. Die Momentan installierte Version ist %$2. Diese Nachricht wird in vier Wochen wieder angezeigt."},"possible": {"header": "Aktualisierung verfügbar","text": "Es ist eine Aktualisierung für %$1 verfügbar.<br>Zur Zeit ist Version %$2 installiert. Die neueste Version ist %$3.","history": "Versionshistorie","noHistory": "Keine Versionshistorie verfügbar.","type": {"feature": "Neuerung(en)","change": "Änderung(en)","bugfix": "Bugfix(e)","language": "Sprache(n)","core": "Ikariam Core","other": "Sonstige"},"button": {"install": "Installieren","hide": "Verbergen"}},"noNewExists": {"header": "Keine Aktualisierung verfügbar","text": "Es ist keine Aktualisierung für %$1 verfügbar. Die neueste Version %$2 ist installiert."}},"notification": {"header": "Informationen","button": {"confirm": "OK","abort": "Abbrechen"}},"optionPanel": {"save": "Einstellungen speichern!","section": {"update": {"title": "Aktualisierungen","label": {"interval": {"description": "In folgenden Zeitabständen nach Aktualisierungen suchen:","option": {"never": "Nie","hour": "1 Stunde","hour12": "12 Stunden","day": "1 Tag","day3": "3 Tage","week": "1 Woche","week2": "2 Wochen","week4": "4 Wochen"}},"notifyLevel": {"description": "Benachrichtige über neue Versionen bis zu diesem Level:","option": {"all": "Alle Versionen","major": "Hauptversionen (x)","minor": "Nebenversionen (x.x)","patch": "Revisionen (x.x.x)"}},"manual": "Nach Aktualisierungen für \"%$1\" suchen!"}},"optionPanelOptions": {"title": "Optionsfenster","label": {"import": "Optionen importieren","export": "Optionen exportieren","reset": "Optionen zurücksetzen","importNotification": {"header": "Importieren","explanation": "Füge das JSON zum importieren in das Feld unten ein und klicke \"OK\". Die Optionen werden dann importiert. Bitte sorge dafür, dass kein Zeichen fehlt, da sonst der Import nicht funktionieren wird."},"exportNotification": {"header": "Exportieren","explanation": "Bitte kopiere das JSON unten. Du kannst es danach auf jedem beliebigen PC importieren, um die Optionen dort zu erhalten. Bitte sorge dafür, dass kein Zeichen fehlt, da sonst der Import nicht funktionieren wird."},"importError": {"header": "Fehler beim importieren!","explanation": "Während dem importieren der Optionen trat ein Fehler auf. Es scheint, dass das JSON fehlerhaft ist. Bitte validiere es (beispielsweise mit <a href=\"http://jsonlint.com/\" target=\"_blank\">JSONLint</a>)."},"resetNotification": {"header": "Optionen zurücksetzen","explanation": "Bist du dir sicher, dass du alle Optionen auf ihren Standardwert zurücksetzen möchtest?"}}}}}},"general": {"successful": "Dein Befehl wurde ausgeführt.","error": "Es gab einen Fehler in deiner Anfrage!","fold": "Einklappen","expand": "Ausklappen","ctrl": "Strg","alt": "Alt","shift": "Umschalt","yes": "Ja","no": "Nein"}} },
  1770. { code: 'gr', settings: {"settings": {"kiloSep":",","decSep":".","ltr":true}}, translations: {"core": {"update": {"notPossible": {"header": "Καμιά διαθέσιμη Ενημέρωση","text": "Δεν είναι δυνατός ο έλεγχος ενημερώσεων για τον κώδικα %$1. Ελέγξτε χειροκίνητα για Ενημερώσεις για τον κώδικα. Η πραγματικά εγκατεστημένη έκδοση είναι η %$2. Αυτό το μήνυμα θα εμφανιστεί ξανά σε 4 εβδομάδες."},"possible": {"header": "Διαθέσιμη Ενημέρωση","text": "Υπάρχει μια διαθέσιμη ενημέρωση για τον κώδικα %$1.<br>Προς το παρόν, υπάρχει εγκατεστημένη η έκδοση %$2. Η νεότερη ΄έκδοση είναι η %$3.","history": "Ιστορικό Έκδοσης","noHistory": "Κανένα ιστορικό έκδοσης διαθέσιμο.","type": {"feature": "Χαρακτηριστικό(ά)","change": "Αλλαγή(ές)","bugfix": "Διόρθωση(εις) σφάλματος(ων)","language": "Γλώσσα(ες)","core": "Πυρήνας Ikariam","other": "Άλλο"},"button": {"install": "Εγκατάσταση","hide": "Απόκρυψη"}},"noNewExists": {"header": "Καμιά διαθέσιμη Ενημέρωση","text": "Δεν υπάρχει καμιά νέα έκδοση διαθέσιμη για τον κώδικα %$1. Η νεότερη έκδοση %$2 είναι εγκατεστημένη."}},"notification": {"header": "Ειδοποίηση κώδικα","button": {"confirm": "Εντάξει","abort": "Παραίτηση"}},"optionPanel": {"save": "Αποθήκευση ρυθμίσεων!","section": {"update": {"title": "Ενημέρωση","label": {"interval": {"description": "Χρόνος επανάληψης για ενημερώσεις:","option": {"never": "Ποτέ","hour": "1 ώρα","hour12": "12 ώρες","day": "1 ημέρα","day3": "3 ημέρες","week": "1 εβδομάδα","week2": "2 εβδομάδες","week4": "4 εβδομάδες"}},"notifyLevel": {"description": "Ειδοποίηση για νέες εκδόσεις μέχρι αυτό το επίπεδο:","option": {"all": "Όλες οι εκδόσεις","major": "Βασική (x)","minor": "Δευτερεύουσα (x.x)","patch": "Διόρθωση (x.x.x)"}},"manual": "Αναζήτηση για ενημερώσεις για «%$1»!"}},"optionPanelOptions": {"title": "Πίνακας Επιλογών","label": {"import": "Εισαγωγή των επιλογών του κώδικα","export": "Εξαγωγή των επιλογών κώδικα","reset": "Επαναφορά των επιλογών κώδικα","importNotification": {"header": "Εισαγωγή","explanation": "Εισάγετε το JSON σας για εισαγωγή στην περιοχή παρακάτω και πατήστε Εντάξει. Οι επιλογές θα εισαχθούν μετά. Σιγουρέψτε ότι κανένας χαρακτήρας δεν λείπει. Διαφορετικά, η εισαγωγή δεν θα λειτουργήσει."},"exportNotification": {"header": "Εξαγωγή","explanation": "Αντιγράψτε το JSON παρακάτω. Μπορείτε να το εισάγετε σε οποιοδήποτε υπολογιστή για να μεταφέρετε τις επιλογές εκεί. Σιγουρέψτε ότι κανένας χαρακτήρας δεν λείπει. Διαφορετικά η εισαγωγή δεν θα λειτουργήσει."},"importError": {"header": "Σφάλμα εισαγωγής!","explanation": "Υπήρξε κάποιο σφάλμα κατά την εισαγωγή των επιλογών. Φαίνεται ότι το JSON είναι κατεστραμένο. Please Επικυρώστε το (π.χ. με το <a href=\"http://jsonlint.com/\" target=\"_blank\">JSONLint</a>)."},"resetNotification": {"header": "Επαναφορά επιλογών","explanation": "Θέλετε, σίγουρα, να επαναφέρετε όλες τις επιλογές του κώδικα στην προεπιλεγμένη τιμή;"}}}}}},"general": {"successful": "Η εντολή σας έχει δρομολογηθεί.","error": "Υπήρξε ένα σφάλμα στο αίτημα σας.","fold": "Δίπλωμα","expand": "Επέκταση","ctrl": "Ctrl","alt": "Alt","shift": "Shift","yes": "Ναι","no": "Όχι"}} },
  1771. { code: 'en', settings: {"settings": {"kiloSep":",","decSep":".","ltr":true}}, translations: {"core": {"update": {"notPossible": {"header":"No Update possible","text":"It is not possible to check for updates for %$1. Please check manually for Updates for the script. The actual installed version is %$2. This message will appear again in four weeks."},"possible": {"header":"Update available","text":"There is an update for %$1 available.<br>At the moment there is version %$2 installed. The newest version is %$3.","history":"Version History","noHistory":"No version history available.","type": {"feature":"Feature(s)","change":"Change(s)","bugfix":"Bugfix(es)","language":"Language(s)","core":"Ikariam Core","other":"Other"},"button": {"install":"Install","hide":"Hide"}},"noNewExists": {"header":"No Update available","text":"There is no new version for %$1 available. The newest version %$2 is installed."}},"notification": {"header":"Script notification","button": {"confirm":"OK","abort":"Abort"}},"optionPanel": {"save":"Save settings!","section": {"update": {"title":"Update","label": {"interval": {"description": "Interval to search for updates:","option": {"never":"Never","hour":"1 hour","hour12":"12 hours","day":"1 day","day3":"3 days","week":"1 week","week2":"2 weeks","week4":"4 weeks"}},"notifyLevel": {"description": "Notify on new script versions up to this level:","option": {"all":"All Versions","major":"Major (x)","minor":"Minor (x.x)","patch":"Patch (x.x.x)"}},"manual":"Search for updates for \"%$1\"!"}},"optionPanelOptions": {"title":"Option Panel","label": {"import":"Import the script options","export":"Export the script options","reset":"Reset the script options","importNotification": {"header":"Import","explanation":"Put your JSON to import in the area below and click OK. The options will be imported then. Please ensure that no character is missing. Otherwise the import will not work."},"exportNotification": {"header":"Export","explanation":"Please copy the JSON below. You can import it on any computer to get the options there. Please ensure that no character is missing. Otherwise the import will not work."},"importError": {"header":"Import error!","explanation":"There was an error while importing the options. It seems that the JSON is broken. Please validate it (e.g. with <a href=\"http://jsonlint.com/\" target=\"_blank\">JSONLint</a>)."},"resetNotification": {"header":"Reset options","explanation":"Are you sure to reset all script options to their default value?"}}}}}},"general": {"successful":"Your order has been carried out.","error":"There was an error in your request.","fold":"Fold","expand":"Expand","ctrl":"Ctrl","alt":"Alt","shift":"Shift","yes":"Yes","no":"No"}} },
  1772. { code: 'fr', settings: {"settings": {"kiloSep":",","decSep":".","ltr":true}}, translations: {"core": {"update": {"notPossible": {"header": "Pas de Mettre de jour possible","text": ""},"possible": {"header": "Update utilisable","text": "","history": "","noHistory": "","type": {"feature": "","change": "","bugfix": "","language": "langue(s)","core": "","other": "autre"},"button": {"install": "","hide": ""}},"noNewExists": {"header": "Pas de Mettre de jour utilisable","text": ""}},"notification": {"header": "","button": {"confirm": "OK","abort": "Abandon"}},"optionPanel": {"save": "","section": {"update": {"title": "Mettre de Jour","label": {"interval": {"description": "","option": {"never": "Jamais","hour": "1 heure","hour12": "12 heures","day": "1 jour","day3": "3 jour","week": "1 semaine","week2": "2 semaines","week4": "4 semaines"}},"notifyLevel": {"description": "","option": {"all": "","major": "","minor": "","patch": ""}},"manual": ""}},"optionPanelOptions": {"title": "","label": {"import": "","export": "","reset": "","importNotification": {"header": "","explanation": ""},"exportNotification": {"header": "","explanation": ""},"importError": {"header": "","explanation": ""},"resetNotification": {"header": "","explanation": ""}}}}}},"general": {"successful": "","error": "","fold": "","expand": "","ctrl": "Ctrl","alt": "Alt","shift": "","yes": "Oui","no": "Non"}} },
  1773. { code: 'it', settings: {"settings": {"kiloSep":",","decSep":".","ltr":true}}, translations: {"core": {"update": {"notPossible": {"header": "niente da aggiornare","text": "Non è possibile verificare la disponibilità di aggiornamenti per %$1. Si prega di controllare manualmente gli aggiornamenti per lo script. La versione attuale installata è %$2. Questo messaggio apparirà di nuovo in quattro settimane."},"possible": {"header": "Aggiornamento disponibile","text": "C'è un aggiornamento per %$1 disponibile. <br> Al momento è la versione %$2 installato. L'ultima versione è %$3.","history": "Cronologia delle versioni","noHistory": "","type": {"feature": "","change": "Cambiamenti","bugfix": "","language": "lingue","core": "Ikariam Core","other": "altri"},"button": {"install": "installare","hide": "nascondere"}},"noNewExists": {"header": "Nessun aggiornamento disponibile","text": "Non ci sono nuova versione disponibile per %$1. La nuova versione %$2 è installato."}},"notification": {"header": "Informazioni degli script","button": {"confirm": "OK","abort": "abortire"}},"optionPanel": {"save": "Salva impostazioni!","section": {"update": {"title": "Aggiornamenti","label": {"interval": {"description": "Intervallo per la ricerca di aggiornamenti:","option": {"never": "mai","hour": "1 ora","hour12": "12 ore","day": "1 giorno","day3": "3 giorni","week": "1 settimana","week2": "2 settimane","week4": "4 settimane"}},"notifyLevel": {"description": "Notifica nuove versioni di script fino a questo livello:","option": {"all": "tutte le versioni","major": "Versione principale (x)","minor": "Versioni minori (x.x)","patch": ""}},"manual": "Cerca aggiornamenti per \"%$1\"!"}},"optionPanelOptions": {"title": "Finestra delle opzioni","label": {"import": "Importare le opzioni di script","export": "le opzioni di esportazione degli script","reset": "Aggiorna le opzioni di script","importNotification": {"header": "importazione","explanation": "Metti il JSON importare nella zona sotto e fare clic su OK. Le opzioni saranno importati poi. Fate attenzione che non manca di carattere. In caso contrario, l'importazione non funziona."},"exportNotification": {"header": "esportazione","explanation": "Si prega di copiare il JSON sotto. È possibile importare su qualsiasi computer per ottenere le opzioni lì. Assicurarsi fatto nessun personaggio manca. In caso contrario, l'importazione non funziona."},"importError": {"header": "","explanation": "Si è verificato un errore durante l'importazione delle opzioni. Sembra che il JSON è rotto. Si prega di confermare che (ad esempio con <a href=\"http://jsonlint.com/\" target=\"_blank\">JSONLint</a>)."},"resetNotification": {"header": "Azzera opzioni","explanation": "Sei sicuro di voler ripristinare tutte le opzioni di script per il loro valore di default?"}}}}}},"general": {"successful": "Tuo ordine è stato effettuato","error": "C'è stato un errore nella tua richiesta.","fold": "","expand": "","ctrl": "","alt": "","shift": "","yes": "sì","no": "No"}} },
  1774. { code: 'lv', settings: {"settings": {"kiloSep":",","decSep":".","ltr":true}}, translations: {"core": {"update": {"notPossible": {"header": "Nav iespējams atjaunināt","text": "Nav iespējams pārbaudīt atjauninājumu pieejamību %$1. Lūdzu, pārbaudiet manuāli skripta atjauninājumus. Pašlaik ir uzstādīta versija %$2. Šis paziņojums atkal parādīsies pēc četrām nedēļām."},"possible": {"header": "Ir pieejams atjauninājums","text": "Ir pieejams %$1 atjauninājums.<br>Pašlaik ir uzstādīta versija %$2. Jaunākā versija ir %$3.","history": "Versijas vēsture:","noHistory": "","type": {"feature": "Jaunumi","change": "Izmaiņas","bugfix": "Kļūdu labojumi","language": "Valodas","core": "Ikariam Core","other": "Citi"},"button": {"install": "Instalēt","hide": "Paslēpt"}},"noNewExists": {"header": "Nav pieejams atjauninājums","text": "Nav pieejama jauna versija %$1. Jaunākā versija %$2 ir jau uzstādīta."}},"notification": {"header": "Skripta paziņojumi","button": {"confirm": "OK","abort": "Atcelt"}},"optionPanel": {"save": "Saglabāt iestatījumus!","section": {"update": {"title": "Atjauninājumi","label": {"interval": {"description": "Intervāls starp atjauninājumu meklēšanu:","option": {"never": "Nekad","hour": "1 stunda","hour12": "12 stundas","day": "1 diena","day3": "3 dienas","week": "1 nedēļa","week2": "2 nedēļas","week4": "4 nedēļas"}},"notifyLevel": {"description": "","option": {"all": "Visas versijas","major": "","minor": "","patch": ""}},"manual": "Meklēt atjauninājumus \"%$1\"!"}},"optionPanelOptions": {"title": "Opcijas","label": {"import": "Importēt skripta opcijas","export": "Eksportēt skripta opcijas","reset": "Atiestatīt skripta opcijas","importNotification": {"header": "Importēt","explanation": ""},"exportNotification": {"header": "Eksportēt","explanation": ""},"importError": {"header": "Importēšanas kļūda!","explanation": ""},"resetNotification": {"header": "Atstatīšanas opcijas","explanation": ""}}}}}},"general": {"successful": "Jūsu rīkojums ir izpildīts.","error": "Jūsu veiktajā pieprasījumā radās kļūda.","fold": "Samazināt","expand": "Paplašināt","ctrl": "Ctrl","alt": "Alt","shift": "Shift","yes": "Jā","no": "Nē"}} },
  1775. { code: 'ru', settings: {"settings": {"kiloSep":",","decSep":".","ltr":true}}, translations: {"core": {"update": {"notPossible": {"header": "У Вас последняя версия скрипта","text": "Невозможно проверить наличие обновлений для %$1. Пожалуйста, проверьте наличие обновлений для скрипта вручную. У Вас установлена версия %$2. Это сообщение появится снова в течение четырех недель."},"possible": {"header": "Доступно обновление","text": "Существует обновления для версии %$1.<br>На данный момент установлена версия %$2. Новейшая версия - %$3.","history": "История версий","noHistory": "Нет истории версий.","type": {"feature": "Особенность(и)","change": "Изменение(я)","bugfix": "Исправление(я)","language": "Язык(и)","core": "Ikariam Core","other": "Другое"},"button": {"install": "Установить","hide": "Скрыть"}},"noNewExists": {"header": "Нет доступных обновлений","text": "У Вас последняя версия скрипта %$1. Новейшая версия - %$2 уже установлена."}},"notification": {"header": "Уведомление скрипта","button": {"confirm": "OK","abort": "Отменить"}},"optionPanel": {"save": "Сохранить настройки!","section": {"update": {"title": "Обновить","label": {"interval": {"description": "Интервал поиска обновлений:","option": {"never": "Никогда","hour": "1 час","hour12": "12 часов","day": "1 день","day3": "3 дня","week": "1 неделя","week2": "2 недели","week4": "4 недели"}},"notifyLevel": {"description": "Уведомлять о новых версиях скрипта до этого уровня:","option": {"all": "Все версии","major": "Мажорная версия (x)","minor": "Минорная версия (x.x)","patch": "Патч или заплатка (x.x.x)"}},"manual": "Поиск обновлений для \"%$1\"!"}},"optionPanelOptions": {"title": "Панель настроек","label": {"import": "Импорт настроек скрипта","export": "Экспорт настроек скрипта","reset": "Сброс настройки скрипта","importNotification": {"header": "Импорт","explanation": "Для импорта настроек скопируйте содержание JSON файла-импорта в поле ниже и нажмите кнопку ОК. Пожалуйста, проверьте, что бы ни один символ не был пропущен. В противном случае импорт работать не будет."},"exportNotification": {"header": "Экспорт","explanation": "Пожалуйста, скопируйте текст из поля ниже в JSON файл-импорта. Вы можете импортировать его на любом компьютере, чтобы восстановить Ваши настройки. Пожалуйста, проверьте, что бы ни один символ не был пропущен. В противном случае импорт работать не будет."},"importError": {"header": "Ошибка импорта","explanation": "Во время импорта настроек произошла ошибка. Возможно JSON файл-импорта поврежден. Пожалуйста, проверьте его (например, с помощью <a href=\"http://jsonlint.com/\" target=\"_blank\">JSONLint</a>)."},"resetNotification": {"header": "Сброс настроек","explanation": "Вы уверены, что хотите сбросить настройки скрипта к их значению по умолчанию?"}}}}}},"general": {"successful": "Ваш приказ был выполнен.","error": "Ошибка в запросе.","fold": "Свернуть","expand": "Развернуть","ctrl": "Ctrl","alt": "Alt","shift": "Shift","yes": "Да","no": "Нет"}} },
  1776. { code: 'tr', settings: {"settings": {"kiloSep":",","decSep":".","ltr":true}}, translations: {"core": {"update": {"notPossible": {"header": "","text": ""},"possible": {"header": "Güncelleştirme mevcut","text": "","history": "Sürüm Geçmişi","noHistory": "Versiyon geçmişi bulunmamakta.","type": {"feature": "Özellik(ler)","change": "Değişiklik(ler)","bugfix": "","language": "Dil(ler)","core": "","other": "Diğer"},"button": {"install": "Kur","hide": "Gizle"}},"noNewExists": {"header": "","text": "Versiyon %$1 için yeni sürüm bulunmamaktadır. En yeni sürüm %$2 yüklendi."}},"notification": {"header": "","button": {"confirm": "ONAY","abort": "İptal et"}},"optionPanel": {"save": "Ayarları kaydet!","section": {"update": {"title": "Güncelle","label": {"interval": {"description": "","option": {"never": "","hour": "1 saat","hour12": "12 saat","day": "1 gün","day3": "3 gün","week": "1 hafta","week2": "2 hafta","week4": "4 hafta"}},"notifyLevel": {"description": "","option": {"all": "Tüm Versiyonlar","major": "","minor": "","patch": "Yama (x.x.x)"}},"manual": "\"%$1\" için güncelleştirmeleri ara!"}},"optionPanelOptions": {"title": "Ayarlar Paneli","label": {"import": "","export": "","reset": "","importNotification": {"header": "İçe aktar","explanation": ""},"exportNotification": {"header": "Dışa aktar","explanation": ""},"importError": {"header": "İçe aktarım hatası!","explanation": ""},"resetNotification": {"header": "","explanation": ""}}}}}},"general": {"successful": "","error": "İsteğiniz gerçekleştirilirken bir hata meydana geldi.","fold": "","expand": "Genişlet","ctrl": "Ctrl","alt": "","shift": "","yes": "Evet","no": "Hayır"}} }
  1777. ];
  1778. for(var i = 0; i < la_language.length; i++) {
  1779. await _addLanguageText(la_language[i].code, la_language[i].translations);
  1780. await _addLanguageText(la_language[i].code, la_language[i].settings);
  1781. }
  1782. _gb_initialized = true;
  1783. };
  1784. /**
  1785. * Set the default language text for the script.
  1786. *
  1787. * @private
  1788. * @inner
  1789. *
  1790. * @return {Promise}
  1791. * A Promise which resolves once the default text is set.
  1792. */
  1793. var _setDefaultText = async function() {
  1794. var lo_merged = await _mergeTexts(_gs_defaultCode);
  1795. if(lo_merged.is_empty === true || lo_merged.not_set === true)
  1796. _go_defaultText = {};
  1797. else
  1798. _go_defaultText = lo_merged;
  1799. };
  1800. /**
  1801. * Set the chosen language text for the script.
  1802. *
  1803. * @private
  1804. * @inner
  1805. *
  1806. * @param {String} is_languageCode
  1807. * The code of the last selected language.
  1808. *
  1809. * @return {Promise}
  1810. * A Promise which resolves once the language text is set.
  1811. */
  1812. var _setText = async function(is_languageCode) {
  1813. if(is_languageCode === _gs_defaultCode)
  1814. await _setDefaultText();
  1815. if(!!_go_registeredLangs[_gs_ikaCode] === true)
  1816. _gs_usedCode = _gs_ikaCode;
  1817. if(is_languageCode === _gs_usedCode) {
  1818. var lo_merged = await _mergeTexts(is_languageCode);
  1819. if(lo_merged.is_empty === true || lo_merged.not_set === true)
  1820. _go_usedText = _go_defaultText;
  1821. else
  1822. _go_usedText = lo_merged;
  1823. }
  1824. };
  1825. /**
  1826. * Merges the texts for a given language.
  1827. *
  1828. * @private
  1829. * @inner
  1830. *
  1831. * @param {String} is_languageCode
  1832. * The code of the language to merge.
  1833. *
  1834. * @return {Promise}
  1835. * A Promise which resolves to the merged texts.
  1836. */
  1837. var _mergeTexts = async function(is_languageCode) {
  1838. var ro_merged = {};
  1839. if(!!_go_registeredLangs[is_languageCode] === true) {
  1840. var lb_initial = true;
  1841. for(var i = 0; i < _go_registeredLangs[is_languageCode].length; i++) {
  1842. var lo_element = _go_registeredLangs[is_languageCode][i];
  1843. if(lo_element.type === 'resource') {
  1844. var lo_resource = await go_self.myGM.getResourceParsed(lo_element.data.name, lo_element.data.url);
  1845. if(!lo_resource.is_error === true) {
  1846. ro_merged = go_self.myGM.merge(ro_merged, lo_resource);
  1847. lb_initial = false;
  1848. }
  1849. } else if(lo_element.type === 'json') {
  1850. ro_merged = go_self.myGM.merge(ro_merged, lo_element.data);
  1851. lb_initial = false;
  1852. }
  1853. }
  1854. if(lb_initial === true)
  1855. ro_merged = { is_empty: true };
  1856. } else {
  1857. ro_merged = { not_set: true };
  1858. }
  1859. return ro_merged;
  1860. };
  1861. /**
  1862. * Return a string which is defined by its placeholder. If the string contains variables defined with %$nr,
  1863. * they are replaced with the content of the array at this index.
  1864. *
  1865. * @private
  1866. * @inner
  1867. *
  1868. * @param {String} is_name
  1869. * The name of the placeholder.
  1870. * @param {?Array.<*>} [ia_variables]
  1871. * An array containing variables to replace the placeholders in the language string.
  1872. * @param {?boolean} [ib_useDefault=false]
  1873. * If the default language should be used instead of the selected.
  1874. *
  1875. * @return {String}
  1876. * The text.
  1877. */
  1878. var _getText = function(is_name, ia_variables, ib_useDefault) {
  1879. // Set the text to the placeholder.
  1880. var rs_text = is_name;
  1881. // Split the placeholder.
  1882. var la_parts = is_name.split('.');
  1883. if(!!la_parts === true) {
  1884. // Set ls_text to the "next level".
  1885. var ls_text = _go_usedText ? _go_usedText[la_parts[0]] : null;
  1886. if(ib_useDefault === true)
  1887. ls_text = _go_defaultText ? _go_defaultText[la_parts[0]] : null;
  1888. // Loop over all parts.
  1889. for(var i = 1; i < la_parts.length; i++) {
  1890. // If the "next level" exists, set txt to it.
  1891. if(ls_text && typeof ls_text[la_parts[i]] != 'undefined') {
  1892. ls_text = ls_text[la_parts[i]];
  1893. } else {
  1894. ls_text = rs_text;
  1895. break;
  1896. }
  1897. }
  1898. // If the text type is not an object, a function or undefined.
  1899. if(typeof ls_text != 'object' && typeof ls_text != 'function' && typeof ls_text != 'undefined')
  1900. rs_text = ls_text + '';
  1901. if(!!ia_variables === true && Array.isArray(ia_variables) === true) {
  1902. for(var i = 0; i < ia_variables.length; i++) {
  1903. var lr_regex = new RegExp('%\\$' + (i + 1), 'g');
  1904. rs_text = rs_text.replace(lr_regex, ia_variables[i] + '');
  1905. }
  1906. }
  1907. }
  1908. if(ib_useDefault === true) {
  1909. return rs_text;
  1910. }
  1911. if(rs_text == is_name || rs_text == "") {
  1912. go_self.con.info('Language.getText: No translation available for "' + is_name + '" in language ' + go_self.Language.usedLanguageCode);
  1913. rs_text = _getText(is_name, ia_variables, true);
  1914. }
  1915. return rs_text;
  1916. };
  1917. /**
  1918. * Registers a new language without resource usage.
  1919. *
  1920. * @private
  1921. * @inner
  1922. *
  1923. * @param {String} is_languageCode
  1924. * The code of the language.
  1925. * @param {json} io_json
  1926. * JSON with the language data.
  1927. *
  1928. * @return {Promise}
  1929. * A Promise which resolves once the language text is added.
  1930. */
  1931. var _addLanguageText = async function(is_languageCode, io_json) {
  1932. if(!_go_registeredLangs[is_languageCode] === true)
  1933. _go_registeredLangs[is_languageCode] = [];
  1934. _go_registeredLangs[is_languageCode].push({
  1935. type: 'json',
  1936. data: io_json
  1937. });
  1938. await _setText(is_languageCode);
  1939. };
  1940. /**
  1941. * Registers a new language resource.
  1942. *
  1943. * @private
  1944. * @inner
  1945. *
  1946. * @param {String} is_languageCode
  1947. * Code of the language.
  1948. * @param {String} is_resourceName
  1949. * Name of the resource.
  1950. * @param {String} is_resourceURL
  1951. * URL, if resources are not supported.
  1952. *
  1953. * @return {Promise}
  1954. * A Promise which resolves once the language resource is set.
  1955. */
  1956. var _registerLanguageResource = async function(is_languageCode, is_resourceName, is_resourceURL) {
  1957. if(!_go_registeredLangs[is_languageCode] === true)
  1958. _go_registeredLangs[is_languageCode] = [];
  1959. _go_registeredLangs[is_languageCode].push({
  1960. type: 'resource',
  1961. data: { name: is_resourceName, url: is_resourceURL }
  1962. });
  1963. await _setText(is_languageCode);
  1964. };
  1965. /*-------------------------------------------*
  1966. * Public variables, functions and settings. *
  1967. *-------------------------------------------*/
  1968. /**
  1969. * Code of the used language.
  1970. *
  1971. * @instance
  1972. * @readonly
  1973. * @name usedLanguageCode
  1974. * @memberof IkariamCore~Language
  1975. *
  1976. * @type {String}
  1977. */
  1978. Object.defineProperty(this, 'usedLanguageCode', { get: function() {
  1979. return _gs_usedCode;
  1980. } });
  1981. /**
  1982. * Name of the used language.
  1983. *
  1984. * @instance
  1985. * @readonly
  1986. * @name usedLanguageName
  1987. * @memberof IkariamCore~Language
  1988. *
  1989. * @type {String}
  1990. */
  1991. Object.defineProperty(this, 'usedLanguageName', { get: function() {
  1992. return _go_codeTranslation[_gs_usedCode];
  1993. } });
  1994. /**
  1995. * Set the default language.
  1996. *
  1997. * @instance
  1998. *
  1999. * @param {String} is_languageCode
  2000. * The code of the default language.
  2001. *
  2002. * @return {Promise}
  2003. * A Promise which resolves once the default language is set.
  2004. */
  2005. this.setDefaultLanguage = async function(is_languageCode) {
  2006. if(_gb_initialized === false) {
  2007. await _initialize();
  2008. }
  2009. _gs_defaultCode = is_languageCode;
  2010. await _setDefaultText();
  2011. };
  2012. /**
  2013. * Registers a new language without resource usage.
  2014. *
  2015. * @instance
  2016. *
  2017. * @param {String} is_languageCode
  2018. * The code of the language.
  2019. * @param {json} io_json
  2020. * JSON with the language data.
  2021. *
  2022. * @return {Promise}
  2023. * A Promise which resolves once the language text is added.
  2024. */
  2025. this.addLanguageText = async function(is_languageCode, io_json) {
  2026. if(_gb_initialized === false) {
  2027. await _initialize();
  2028. }
  2029. await _addLanguageText(is_languageCode, io_json, false);
  2030. };
  2031. /**
  2032. * Registers a new language resource.
  2033. *
  2034. * @instance
  2035. *
  2036. * @param {String} is_languageCode
  2037. * Code of the language.
  2038. * @param {String} is_resourceName
  2039. * Name of the resource.
  2040. * @param {String} is_resourceURL
  2041. * URL, if resources are not supported.
  2042. *
  2043. * @return {Promise}
  2044. * A Promise which resolves once the language resource is set.
  2045. */
  2046. this.registerLanguageResource = async function(is_languageCode, is_resourceName, is_resourceURL) {
  2047. if(_gb_initialized === false) {
  2048. await _initialize();
  2049. }
  2050. await _registerLanguageResource(is_languageCode, is_resourceName, is_resourceURL, false);
  2051. };
  2052. /**
  2053. * Return a string which is defined by its placeholder. If the string contains variables defined with %$nr,
  2054. * they are replaced with the content of the array at this index.
  2055. *
  2056. * @instance
  2057. *
  2058. * @param {String} is_name
  2059. * The name of the placeholder.
  2060. * @param {?Array.<*>} [ia_variables]
  2061. * An array containing variables to replace the placeholders in the language string.
  2062. *
  2063. * @return {String}
  2064. * The text.
  2065. */
  2066. this.getText = function(is_name, ia_variables) {
  2067. return _getText(is_name, ia_variables);
  2068. };
  2069. /**
  2070. * Synonymous function for {@link IkariamCore~Language#getText}.<br>
  2071. *
  2072. * @instance
  2073. *
  2074. * @see IkariamCore~Language#getText
  2075. *
  2076. * @param {String} is_name
  2077. * The name of the placeholder.
  2078. * @param {?Array.<*>} [ia_variables]
  2079. * An array containing variables to replace the placeholders in the language string.
  2080. *
  2081. * @return {String}
  2082. * The text.
  2083. */
  2084. this.$ = function(is_name, ia_variables) {
  2085. return this.getText(is_name, ia_variables);
  2086. };
  2087. /*---------------------------------------------------------------------*
  2088. * Types for documentation purposes (e.g. callback functions, objects) *
  2089. *---------------------------------------------------------------------*/
  2090. /**
  2091. * Storage for language settings.
  2092. *
  2093. * @callback IkariamCore~Language~LanguageSettings
  2094. *
  2095. * @private
  2096. * @inner
  2097. *
  2098. * @param {String} type
  2099. * The type of the language resources. Currently supported: resource, json
  2100. * @param {({name: String, url: String}|json)} data
  2101. * The data required to fetch the translations of this language.
  2102. */
  2103. }
  2104. /**
  2105. * Functions for localization of the script.
  2106. *
  2107. * @instance
  2108. *
  2109. * @type IkariamCore~Language
  2110. */
  2111. this.Language = new Language();
  2112. this.con.logTimeStamp('IkariamCore.Language created');
  2113. /**
  2114. * Instantiate a new set of Ikariam specific functions.
  2115. *
  2116. * @inner
  2117. *
  2118. * @class
  2119. * @classdesc Ikariam specific functions.
  2120. */
  2121. function Ikariam() {
  2122. /*-------------------------------------------*
  2123. * Public variables, functions and settings. *
  2124. *-------------------------------------------*/
  2125. /**
  2126. * Name of the shown view (world, island, town).
  2127. *
  2128. * @instance
  2129. * @readonly
  2130. * @name view
  2131. * @memberof IkariamCore~Ikariam
  2132. *
  2133. * @type {String}
  2134. */
  2135. Object.defineProperty(this, 'view', { get: function() {
  2136. var ls_viewId = go_self.myGM.$('body').id;
  2137. if(ls_viewId == 'worldmap_iso')
  2138. return 'world';
  2139. if(ls_viewId == 'island')
  2140. return 'island';
  2141. if(ls_viewId == 'city')
  2142. return 'town';
  2143. return '';
  2144. } });
  2145. /**
  2146. * All possible view names.
  2147. *
  2148. * @instance
  2149. * @readonly
  2150. * @name viewNames
  2151. * @memberof IkariamCore~Ikariam
  2152. *
  2153. * @type {Array.<String>}
  2154. */
  2155. Object.defineProperty(this, 'viewNames', { get: function() {
  2156. return ['world', 'island', 'town'];
  2157. } });
  2158. /**
  2159. * All possible resource names.
  2160. *
  2161. * @instance
  2162. * @readonly
  2163. * @name resourceNames
  2164. * @memberof IkariamCore~Ikariam
  2165. *
  2166. * @type {Array.<String>}
  2167. */
  2168. Object.defineProperty(this, 'resourceNames', { get: function() {
  2169. return ['wood', 'wine', 'marble', 'glass', 'sulfur'];
  2170. } });
  2171. /**
  2172. * Code consisting of server id and country code.<br>
  2173. * Structure: <code>&lt;country-code&gt;_&lt;server-id&gt;</code>
  2174. *
  2175. * @instance
  2176. * @readonly
  2177. * @name serverCode
  2178. * @memberof IkariamCore~Ikariam
  2179. *
  2180. * @type {String}
  2181. */
  2182. Object.defineProperty(this, 'serverCode', { get: function() {
  2183. var la_code = top.location.host.match(/^s([0-9]+)-([a-zA-Z]+)\.ikariam\.gameforge\.com$/);
  2184. if(!!la_code)
  2185. return la_code[2] + '_' + la_code[1];
  2186. return 'undefined';
  2187. } });
  2188. /**
  2189. * Code consisting of player id, server id and country code.<br>
  2190. * Structure: <code>&lt;country-code&gt;_&lt;server-id&gt;_&lt;player-id&gt;</code>
  2191. *
  2192. * @instance
  2193. * @readonly
  2194. * @name playerCode
  2195. * @memberof IkariamCore~Ikariam
  2196. *
  2197. * @type {String}
  2198. */
  2199. Object.defineProperty(this, 'playerCode', { get: function() {
  2200. var ls_serverCode = this.serverCode;
  2201. var ls_playerId = go_self.ika.getModel().avatarId;
  2202. if(ls_serverCode !== 'undefined')
  2203. return ls_serverCode + '_' + ls_playerId;
  2204. return 'undefined';
  2205. } });
  2206. /**
  2207. * Parses a string number to an int value.
  2208. *
  2209. * @instance
  2210. *
  2211. * @param {String} is_text
  2212. * The number to format.
  2213. *
  2214. * @return {int}
  2215. * The formatted value.
  2216. */
  2217. this.getInt = function(is_text) {
  2218. var ls_text = is_text + '';
  2219. return parseInt(ls_text.replace(/(\.|,)/g, ''));
  2220. };
  2221. /**
  2222. * Formats a number to the format which is used in Ikariam.
  2223. *
  2224. * @param {int} ii_number
  2225. * The number to format.
  2226. * @param {?(boolean|Object.<String, boolean>)} [im_addColor={ positive: false, negative: true }]
  2227. * If the number should be colored.
  2228. * @param {?boolean} [ib_usePlusSign=false]
  2229. * If a plus sign should be used for positive numbers.
  2230. *
  2231. * @return {String}
  2232. * The formated number.
  2233. */
  2234. this.formatToIkaNumber = function(ii_number, im_addColor, ib_usePlusSign) {
  2235. var rs_text = ii_number + '';
  2236. // Set a seperator every 3 digits from the end.
  2237. rs_text = rs_text.replace(/(\d)(?=(\d{3})+\b)/g, '$1' + go_self.Language.$('settings.kiloSep'));
  2238. if(ii_number < 0 && !(im_addColor == false || (im_addColor && im_addColor.negative == false))) {
  2239. rs_text = '<span class="red bold">' + rs_text + '</span>';
  2240. }
  2241. if(ii_number > 0) {
  2242. rs_text = (ib_usePlusSign ? '+' : '') + rs_text;
  2243. if(!!(im_addColor == true || (im_addColor && im_addColor.positive == true))) {
  2244. rs_text = '<span class="green bold">' + rs_text + '</span>';
  2245. }
  2246. }
  2247. return rs_text;
  2248. };
  2249. /**
  2250. * Shows a hint to the user.
  2251. *
  2252. * @instance
  2253. *
  2254. * @param {String} is_located
  2255. * The location of the hint.<br>
  2256. * Possible values: <code>cityAdvisor</code>, <code>militaryAdvisor</code>, <code>researchAdvisor</code>, <code>diplomacyAdvisor</code>, <code>clickedElement</code>, <code>committedElement</code>
  2257. * @param {String} is_type
  2258. * The type of the hint.<br>
  2259. * Possible values: <code>confirm</code>, <code>error</code>, <code>neutral</code>, <code>followMouse</code>
  2260. * @param {String} is_text
  2261. * The hint text.
  2262. * @param {?String} [is_bindTo=null]
  2263. * The JQuery selector of the element the tooltip should be bound to (only used if location = committedElement).
  2264. * @param {?boolean} [ib_hasAutoWidth=false]
  2265. * If the message has auto width (only used if type = followMouse).
  2266. */
  2267. this.showTooltip = function(is_located, is_type, is_text, is_bindTo, ib_hasAutoWidth) {
  2268. // Get the message location.
  2269. var li_location = -1;
  2270. switch(is_located) {
  2271. case 'cityAdvisor':
  2272. li_location = 1;
  2273. break;
  2274. case 'militaryAdvisor':
  2275. li_location = 2;
  2276. break;
  2277. case 'researchAdvisor':
  2278. li_location = 3;
  2279. break;
  2280. case 'diplomacyAdvisor':
  2281. li_location = 4;
  2282. break;
  2283. case 'clickedElement':
  2284. li_location = 5;
  2285. break;
  2286. case 'committedElement':
  2287. li_location = 6;
  2288. break;
  2289. }
  2290. // Get the message type.
  2291. var li_type = -1;
  2292. switch(is_type) {
  2293. case 'confirm':
  2294. li_type = 10;
  2295. break;
  2296. case 'error':
  2297. li_type = 11;
  2298. break;
  2299. case 'neutral':
  2300. li_type = 12;
  2301. break;
  2302. case 'followMouse':
  2303. li_type = 13;
  2304. break;
  2305. }
  2306. go_self.ika.controller.tooltipController.bindBubbleTip(li_location, li_type, is_text, null, is_bindTo, ib_hasAutoWidth);
  2307. };
  2308. /**
  2309. * Creates new checkboxes in Ikariam style and adds them to a parent.
  2310. *
  2311. * @instance
  2312. *
  2313. * @see IkariamCore~myGM#addCheckboxes
  2314. *
  2315. * @param {Element} ie_parent
  2316. * The parent of the new checkboxes.
  2317. * @param {Array.<IkariamCore~myGM~NewCheckboxData>} ia_cbData
  2318. * An array containing the data of each checkbox.
  2319. */
  2320. this.addCheckboxes = function(ie_parent, ia_cbData) {
  2321. go_self.myGM.addCheckboxes(ie_parent, ia_cbData);
  2322. // Replace the checkboxes for better appearance.
  2323. go_self.ika.controller.replaceCheckboxes();
  2324. };
  2325. /**
  2326. * Creates a new radio button group in ikariam style and adds it to a parent table.
  2327. *
  2328. * @instance
  2329. *
  2330. * @see IkariamCore~myGM#addRadios
  2331. *
  2332. * @param {Element} ie_parentTable
  2333. * The parent table of the new select field.
  2334. * @param {String} is_name
  2335. * The last part of the name of the radio button group.
  2336. * @param {(String|int)} im_checked
  2337. * The value of the selected option.
  2338. * @param {Array.<IkariamCore~myGM~ValueAndLabel>} ia_options
  2339. * An array with the names an values of the options.
  2340. * @param {String} is_labelText
  2341. * The text of the select label.
  2342. */
  2343. this.addRadios = function(ie_parentTable, is_name, im_checked, ia_options, is_labelText) {
  2344. go_self.myGM.addRadios(ie_parentTable, is_name, im_checked, ia_options, is_labelText);
  2345. // Replace the radiobuttons for better appearance.
  2346. go_self.ika.controller.replaceCheckboxes();
  2347. };
  2348. /**
  2349. * Creates a new select field in ikariam style and adds it to a parent table.
  2350. *
  2351. * @instance
  2352. *
  2353. * @see IkariamCore~myGM#addSelect
  2354. *
  2355. * @param {Element} ie_parentTable
  2356. * The parent table of the new select field.
  2357. * @param {String} is_id
  2358. * The last part of the id of the select field.
  2359. * @param {(String|int)} im_selected
  2360. * The value of the selected option.
  2361. * @param {Array.<IkariamCore~myGM~ValueAndLabel>} ia_options
  2362. * An array with the names an values of the options.
  2363. * @param {String} is_labelText
  2364. * The text of the select label.
  2365. */
  2366. this.addSelect = function(ie_parentTable, is_id, im_selected, ia_options, is_labelText) {
  2367. go_self.myGM.addSelect(ie_parentTable, is_id, im_selected, ia_options, is_labelText);
  2368. // Replace the dropdown for better appearance.
  2369. go_self.ika.controller.replaceDropdownMenus();
  2370. };
  2371. }
  2372. /**
  2373. * Ikariam specific functions like converting a number from Ikariam format to int.
  2374. *
  2375. * @instance
  2376. *
  2377. * @type IkariamCore~Ikariam
  2378. */
  2379. this.Ikariam = new Ikariam();
  2380. this.con.logTimeStamp('IkariamCore.Ikariam created');
  2381. /**
  2382. * Instantiate the handler.
  2383. *
  2384. * @inner
  2385. *
  2386. * @class
  2387. * @classdesc Handler for callbacks for processing DOM modification events.
  2388. */
  2389. function Observer() {
  2390. /*--------------------------------------------*
  2391. * Private variables, functions and settings. *
  2392. *--------------------------------------------*/
  2393. /**
  2394. * Storage for MutationObserver.
  2395. *
  2396. * @private
  2397. * @inner
  2398. *
  2399. * @type MutationObserver
  2400. */
  2401. var _go_MutationObserver = MutationObserver || WebKitMutationObserver;
  2402. /**
  2403. * If the MutationObserver can be used or if an workaround must be used.
  2404. *
  2405. * @private
  2406. * @inner
  2407. *
  2408. * @type boolean
  2409. */
  2410. var _gb_canUseObserver = !!_go_MutationObserver;
  2411. /**
  2412. * List to store the created observers.
  2413. *
  2414. * @private
  2415. * @inner
  2416. *
  2417. * @type Object.<String, MutationObserver>
  2418. */
  2419. var _go_observerList = {};
  2420. /*-------------------------------------------*
  2421. * Public variables, functions and settings. *
  2422. *-------------------------------------------*/
  2423. /**
  2424. * Adds a new observer for DOM modification events. If it is possible use MutationObservers. More about the
  2425. * Mutation observer can be found here: {@link https://developer.mozilla.org/en-US/docs/DOM/MutationObserver Mutation Observer on MDN}.<br>
  2426. * If it's not possible to use a MutationObserver a DOMSubtreeModified or DOMAttrModified event listener is used.
  2427. *
  2428. * @instance
  2429. *
  2430. * @param {String} is_id
  2431. * The id to store the observer.
  2432. * @param {element} ie_target
  2433. * The target to observe.
  2434. * @param {Array.<*>} io_options
  2435. * Options for the observer. All possible options can be found here: {@link https://developer.mozilla.org/en-US/docs/DOM/MutationObserver#MutationObserverInit MutationObserver on MDN}
  2436. * @param {IkariamCore~Observer~MutationCallback} if_callback
  2437. * The callback for the mutation observer.<br>
  2438. * @param {IkariamCore~Observer~NoMutationCallback} if_noMutationObserverCallback
  2439. * The callback if the use of the mutation observer is not possible and DOMAttrModified / DOMSubtreeModified is used instead.<br>
  2440. */
  2441. this.add = function(is_id, ie_target, io_options, if_callback, if_noMutationObserverCallback) {
  2442. var lo_observer;
  2443. if(!!ie_target) {
  2444. // If the MutationObserver can be used, do so.
  2445. if(_gb_canUseObserver) {
  2446. lo_observer = new _go_MutationObserver(if_callback);
  2447. lo_observer.observe(ie_target, io_options);
  2448. if(!_go_observerList[is_id]) {
  2449. _go_observerList[is_id] = lo_observer;
  2450. } else {
  2451. go_self.con.warn('Observer.add: Id "' + is_id + '" already used for observer, please choose another one!');
  2452. }
  2453. // Otherwise use the event listener.
  2454. } else {
  2455. if(io_options.attributes) {
  2456. ie_target.addEventListener('DOMAttrModified', if_noMutationObserverCallback, false);
  2457. }
  2458. if(io_options.characterData || io_options.childList || io_options.subtree) {
  2459. ie_target.addEventListener('DOMSubtreeModified', if_noMutationObserverCallback, false);
  2460. }
  2461. }
  2462. } else {
  2463. go_self.con.warn('Observer.add: Observer target not defined! id: ' + is_id);
  2464. }
  2465. };
  2466. /**
  2467. * Removes the observer given by the id. If the use of MutationObserver is not possible, this function can not be used.
  2468. *
  2469. * @instance
  2470. *
  2471. * @param {String} is_id
  2472. * The id of the observer to remove.
  2473. */
  2474. this.remove = function(is_id) {
  2475. // If the observer is set.
  2476. if(_gb_canUseObserver && _go_observerList[is_id]) {
  2477. var lo_observer = _go_observerList[is_id];
  2478. lo_observer.disconnect();
  2479. delete _go_observerList[is_id];
  2480. } else if(!_gb_canUseObserver) {
  2481. go_self.con.warn('Observer.remove: It is not possible to use MutationObservers so Observer.remove can not be used.');
  2482. }
  2483. };
  2484. /*---------------------------------------------------------------------*
  2485. * Types for documentation purposes (e.g. callback functions, objects) *
  2486. *---------------------------------------------------------------------*/
  2487. /**
  2488. * The callback for the mutation observer.
  2489. *
  2490. * @callback IkariamCore~Observer~MutationCallback
  2491. *
  2492. * @param {MutationRecord} mutations
  2493. * The mutations which occurred.
  2494. */
  2495. /**
  2496. * The callback if no mutation observer could be used.
  2497. *
  2498. * @callback IkariamCore~Observer~NoMutationCallback
  2499. */
  2500. }
  2501. /**
  2502. * Handler for callbacks after modification of DOM elements.
  2503. *
  2504. * @instance
  2505. *
  2506. * @type IkariamCore~Observer
  2507. */
  2508. this.Observer = new Observer();
  2509. this.con.logTimeStamp('IkariamCore.Observer created');
  2510. /**
  2511. * Instantiate a new set of refresh functions.
  2512. *
  2513. * @inner
  2514. *
  2515. * @class
  2516. * @classdesc Handles functions that should run on Ikariam popups and after actualizations of the page data.
  2517. */
  2518. function RefreshHandler() {
  2519. /*--------------------------------------------*
  2520. * Private variables, functions and settings. *
  2521. *--------------------------------------------*/
  2522. /**
  2523. * Storage for the actualization callbacks.<br>
  2524. * Architecture:<br>
  2525. * <pre>_go_callbacks = {
  2526. * popupId: {
  2527. * callbackId: callback
  2528. * }
  2529. * }</pre>
  2530. *
  2531. * @private
  2532. * @inner
  2533. *
  2534. * @type Object.<String, Object.<String, function>>
  2535. */
  2536. var _go_callbacks = {};
  2537. /**
  2538. * Handles the call of the callback functions for the actualization.
  2539. *
  2540. * @private
  2541. * @inner
  2542. */
  2543. var _handleActualisation = function() {
  2544. // Run the callbacks for every reload.
  2545. if(_go_callbacks['*']) {
  2546. go_self.myGM.forEach(_go_callbacks['*'], function(is_key, if_callback) {
  2547. if_callback();
  2548. });
  2549. }
  2550. // If the script was already executed on this popup.
  2551. var lb_isAlreadyExecutedPopup = !!go_self.myGM.$('#' + go_self.myGM.prefix + 'alreadyExecutedPopup');
  2552. var le_popup = go_self.myGM.$('.templateView');
  2553. if(le_popup && !lb_isAlreadyExecutedPopup) {
  2554. // Run the callbacks for every popup opening.
  2555. if(_go_callbacks['%']) {
  2556. go_self.myGM.forEach(_go_callbacks['%'], function(is_key, if_callback) {
  2557. if_callback();
  2558. });
  2559. }
  2560. go_self.myGM.addElement('input', go_self.myGM.$('.mainContent', le_popup), { 'id': 'alreadyExecutedPopup', 'type': 'hidden' });
  2561. var ls_popupId = le_popup ? le_popup.id.replace('_c', '') : '';
  2562. if(_go_callbacks[ls_popupId]) {
  2563. go_self.myGM.forEach(_go_callbacks[ls_popupId], function(is_key, if_callback) {
  2564. if_callback();
  2565. });
  2566. }
  2567. }
  2568. };
  2569. /**
  2570. * Callback for MutationObserver for calling the popup handler.
  2571. *
  2572. * @private
  2573. * @inner
  2574. *
  2575. * @param {MutationRecord} la_mutations
  2576. * All recorded mutations.
  2577. */
  2578. var _callback = function(la_mutations) {
  2579. la_mutations.forEach(function(io_mutation) {
  2580. if(io_mutation.target.getAttribute('style').search(/display: none/i) != -1) {
  2581. // Timeout to have access to GM_ funtions.
  2582. setTimeout(_handleActualisation, 0);
  2583. }
  2584. });
  2585. };
  2586. /**
  2587. * Callback for calling the popup handler if the MutationObserver could not be used.
  2588. *
  2589. * @private
  2590. * @inner
  2591. *
  2592. * @param {Event} io_event
  2593. * The called event.
  2594. */
  2595. var _callbackNoMutationObserver = function(io_event) {
  2596. if(io_event.attrChange == MutationEvent.MODIFICATION) {
  2597. if(io_event.attrName.IC.trim() == 'style' && io_event.newValue.search(/display: none/i) != -1) {
  2598. // Timeout to have access to GM_ funtions.
  2599. setTimeout(_handleActualisation, 0);
  2600. }
  2601. }
  2602. };
  2603. /*-------------------------------------------*
  2604. * Public variables, functions and settings. *
  2605. *-------------------------------------------*/
  2606. /**
  2607. * Add a new popup handler.
  2608. *
  2609. * @instance
  2610. *
  2611. * @param {(String|Array.<String>)} im_popupId
  2612. * The id(s) of the popup(s) where the callback should be called (without '_c' at the end).<br>
  2613. * Set to '*' for calling at every actualization, not just popups. Set to '%' for calling on every popup.
  2614. * @param {String} is_callbackId
  2615. * The id of the callback. This must be unique for a popup.
  2616. * @param {function} if_callback
  2617. * The callback which should be called.</code>
  2618. */
  2619. this.add = function(im_popupId, is_callbackId, if_callback) {
  2620. if(Array.isArray(im_popupId) === true) {
  2621. for(var i = 0; i < im_popupId.length; i++) {
  2622. this.add(im_popupId[i], is_callbackId, if_callback);
  2623. }
  2624. return;
  2625. }
  2626. if(!_go_callbacks[im_popupId]) {
  2627. _go_callbacks[im_popupId] = {};
  2628. }
  2629. if(!_go_callbacks[im_popupId][is_callbackId]) {
  2630. _go_callbacks[im_popupId][is_callbackId] = if_callback;
  2631. } else {
  2632. go_self.con.warn('RefreshHandler.add: Id set "' + im_popupId + '|' + is_callbackId + '" already used for observer, please choose another one!');
  2633. }
  2634. };
  2635. /**
  2636. * Removes a popup handler.
  2637. *
  2638. * @instance
  2639. *
  2640. * @param {(String|Array.<String>)} im_popupId
  2641. * The id(s) of the popup(s) where the callback was called (without '_c' at the end).
  2642. * Set to '*' for callbacks on every actualisation, not just popups. Set to '%' for callbacks on every popup.
  2643. * @param {String} is_callbackId
  2644. * The id of the callback. This must be unique for a popup.
  2645. */
  2646. this.remove = function(im_popupId, is_callbackId) {
  2647. if(Array.isArray(im_popupId) === true) {
  2648. for(var i = 0; i < im_popupId.length; i++) {
  2649. this.remove(im_popupId[i], is_callbackId);
  2650. }
  2651. return;
  2652. }
  2653. if(_go_callbacks[im_popupId] && _go_callbacks[im_popupId][is_callbackId]) {
  2654. delete _go_callbacks[im_popupId][is_callbackId];
  2655. }
  2656. };
  2657. /*----------------------------------------------------*
  2658. * Register the observer and handle popups on startup *
  2659. *----------------------------------------------------*/
  2660. // Add the observer for the popups.
  2661. go_self.Observer.add('actualisationHandler', go_self.myGM.$('#loadingPreview'), { attributes: true, attributeFilter: ['style'] }, _callback, _callbackNoMutationObserver);
  2662. // Execute the handler on popups which are shown on startup.
  2663. setTimeout(_handleActualisation, 1000);
  2664. }
  2665. /**
  2666. * Handler for functions that should run on Ikariam popups.
  2667. *
  2668. * @instance
  2669. *
  2670. * @type IkariamCore~RefreshHandler
  2671. */
  2672. this.RefreshHandler = new RefreshHandler();
  2673. this.con.logTimeStamp('IkariamCore.RefreshHandler created');
  2674. /**
  2675. * Instantiate a new set of options / settings functions.
  2676. *
  2677. * @inner
  2678. *
  2679. * @class
  2680. * @classdesc Handles options the user can set, provides a "panel" for them to change them.
  2681. */
  2682. function Options() {
  2683. /*--------------------------------------------*
  2684. * Private variables, functions and settings. *
  2685. *--------------------------------------------*/
  2686. /**
  2687. * Enum for the level of specificity an option can have.
  2688. *
  2689. * @private
  2690. * @inner
  2691. * @readonly
  2692. *
  2693. * @enum {IkariamCore~Options~SpecificityLevelEnum}
  2694. */
  2695. var _gec_SpecificityLevel = Object.freeze({
  2696. GLOBAL: '1',
  2697. SERVER: '2',
  2698. PLAYER: '3'
  2699. });
  2700. /**
  2701. * Storage for option wrapper visibility.
  2702. *
  2703. * @private
  2704. * @inner
  2705. *
  2706. * @type Array.<boolean>
  2707. */
  2708. var _go_optionWrapperVisibility = {};
  2709. /**
  2710. * Storage for option wrappers.
  2711. *
  2712. * @private
  2713. * @inner
  2714. *
  2715. * @type Object
  2716. */
  2717. var _go_wrapper = {};
  2718. /**
  2719. * Storage for option wrapper order. (Order in which the wrappers are shown)
  2720. *
  2721. * @private
  2722. * @inner
  2723. *
  2724. * @type Array.<String>
  2725. */
  2726. var _ga_wrapperOrder = new Array();
  2727. /**
  2728. * Storage for the saved options. Gets filled on startup.
  2729. *
  2730. * @private
  2731. * @inner
  2732. *
  2733. * @type Object
  2734. */
  2735. var _go_savedOptions = null;
  2736. /**
  2737. * Storage for the options.
  2738. *
  2739. * @private
  2740. * @inner
  2741. *
  2742. * @type Object
  2743. */
  2744. var _go_options = {};
  2745. /**
  2746. * Storage for the id of the next <code>&lt;hr&gt;</code> element to create.
  2747. *
  2748. * @private
  2749. * @inner
  2750. *
  2751. * @type int
  2752. */
  2753. var _gi_lineId = 0;
  2754. /**
  2755. * Returns the prefix string for a level of specificity.
  2756. *
  2757. * @private
  2758. * @inner
  2759. *
  2760. * @param {int} ii_specificityLevel
  2761. * The specificity level (One of values of {@link IkariamCore~Options~SpecificityLevelEnum})
  2762. *
  2763. * @return {String}
  2764. * The prefix for this level of specificity.
  2765. */
  2766. var _getSpecificityPrefix = function(ii_specificityLevel) {
  2767. var rv_specificityPrefix = '';
  2768. switch(ii_specificityLevel) {
  2769. case _gec_SpecificityLevel.GLOBAL:
  2770. rv_specificityPrefix = '';
  2771. break;
  2772. case _gec_SpecificityLevel.SERVER:
  2773. rv_specificityPrefix = go_self.Ikariam.serverCode;
  2774. break;
  2775. case _gec_SpecificityLevel.PLAYER:
  2776. rv_specificityPrefix = go_self.Ikariam.playerCode;
  2777. break;
  2778. }
  2779. return rv_specificityPrefix;
  2780. };
  2781. /**
  2782. * Add a element to a wrapper. ("generic function")
  2783. *
  2784. * @private
  2785. * @inner
  2786. *
  2787. * @param {String} is_type
  2788. * The type of the element. Used for replacement - only elements with same type can be replaced.
  2789. * @param {String} is_id
  2790. * The id of the element.
  2791. * @param {String} is_wrapperId
  2792. * The id of the wrapper for the element.
  2793. * @param {(String|int)} im_table
  2794. * The id of the table in the wrapper where the element should be added.
  2795. * @param {IkariamCore~Options~CreateCallback} if_create
  2796. * Callback to create the element.
  2797. * @param {IkariamCore~Options~AddElementOptions} io_options
  2798. * Options for the element.
  2799. *
  2800. * @return {Promise}
  2801. * A Promise which resolves once the element is added.
  2802. */
  2803. var _addElement = async function(is_type, is_id, is_wrapperId, im_table, if_create, io_options) {
  2804. if(_go_savedOptions === null) {
  2805. _go_savedOptions = await go_self.myGM.getValue('optionPanel_options', {});
  2806. }
  2807. if(_go_wrapper[is_wrapperId]) {
  2808. if(_go_wrapper[is_wrapperId].elements[is_id] && io_options.replace !== true) {
  2809. go_self.con.warn('Options.addElement: Element with id "' + is_id + '" already defined. Wrapper id: ' + is_wrapperId);
  2810. } else if(io_options.replace === true && _go_wrapper[is_wrapperId].elements[is_id] && _go_wrapper[is_wrapperId].elements[is_id].type === is_type) {
  2811. go_self.con.warn('Options.addElement: Element with id "' + is_id + '" not replaced. ' +
  2812. 'Different type (old: ' + _go_wrapper[is_wrapperId].elements[is_id].type + ', new: ' + is_type + '). Wrapper id: ' + is_wrapperId);
  2813. } else {
  2814. var lo_options = io_options;
  2815. if(lo_options.replace === true && !_go_wrapper[is_wrapperId].elements[is_id]) {
  2816. delete lo_options.replace;
  2817. go_self.con.info('Options.addElement: Element with id "' + is_id + '" not existant. Element was created instead of replaced. Wrapper id: ' + is_wrapperId);
  2818. }
  2819. var lo_newElement = { table: im_table + '', create: if_create, specificity: (!!lo_options.specificity === true ? lo_options.specificity : _gec_SpecificityLevel.GLOBAL) };
  2820. if(lo_options.replace === true)
  2821. lo_newElement.specificity = _go_wrapper[is_wrapperId].elements[is_id].specificity;
  2822. var ls_specificityPrefix = _getSpecificityPrefix(lo_newElement.specificity);
  2823. if(!!lo_options.createOptions === true)
  2824. lo_newElement.options = lo_options.createOptions;
  2825. if(lo_options.defaultValue !== undefined) {
  2826. lo_newElement.defaultValue = lo_options.defaultValue;
  2827. if(_go_savedOptions[is_wrapperId] && (_go_savedOptions[is_wrapperId][is_id] || _go_savedOptions[is_wrapperId][is_id] === false)) {
  2828. _go_options[is_wrapperId][is_id] = _go_savedOptions[is_wrapperId][is_id];
  2829. if(ls_specificityPrefix.length > 0 && !_go_options[is_wrapperId][is_id][ls_specificityPrefix] && _go_options[is_wrapperId][is_id][ls_specificityPrefix] !== false) {
  2830. _go_options[is_wrapperId][is_id][ls_specificityPrefix] = lo_options.defaultValue;
  2831. }
  2832. } else {
  2833. if(ls_specificityPrefix.length > 0) {
  2834. _go_options[is_wrapperId][is_id] = {};
  2835. _go_options[is_wrapperId][is_id][ls_specificityPrefix] = lo_options.defaultValue;
  2836. } else {
  2837. _go_options[is_wrapperId][is_id] = lo_options.defaultValue;
  2838. }
  2839. }
  2840. }
  2841. if(!!lo_options.saveCallback === true)
  2842. lo_newElement.save = lo_options.saveCallback;
  2843. if(!!lo_options.changeCallback === true) {
  2844. lo_newElement.changeCallback = lo_options.changeCallback;
  2845. // Run the callback also when registering.
  2846. setTimeout(function() {
  2847. var lm_value = _go_options[is_wrapperId][is_id];
  2848. if(ls_specificityPrefix.length > 0)
  2849. lm_value = lm_value[ls_specificityPrefix];
  2850. lo_options.changeCallback(lm_value, lm_value);
  2851. }, 0);
  2852. }
  2853. _go_wrapper[is_wrapperId].elements[is_id] = lo_newElement;
  2854. if(lo_options.replace !== true) {
  2855. _go_wrapper[is_wrapperId].elementOrder.IC.insert(is_id, lo_options.position);
  2856. }
  2857. }
  2858. } else {
  2859. go_self.con.warn('Options.addElement: Wrapper with id "' + is_wrapperId + '" not defined. Element id: ' + is_id);
  2860. }
  2861. };
  2862. /**
  2863. * Save the content of <code>_go_options</code>.
  2864. *
  2865. * @private
  2866. * @inner
  2867. *
  2868. * @param {boolean} showNoSuccessHint
  2869. * If the success hint should not be shown.
  2870. */
  2871. var _saveOptions = function(ib_showNoSuccessHint) {
  2872. go_self.myGM.setValue('optionPanel_options', _go_options).then(function() {
  2873. _go_savedOptions = _go_options;
  2874. if(!ib_showNoSuccessHint === true) {
  2875. go_self.Ikariam.showTooltip('cityAdvisor', 'confirm', go_self.Language.$('general.successful'));
  2876. }
  2877. });
  2878. };
  2879. /**
  2880. * Store the actual value of each option to <code>_option</code> and call <code>_saveOptions</code>.
  2881. *
  2882. * @private
  2883. * @inner
  2884. */
  2885. var _savePanelOptions = function() {
  2886. // Store the value of each option element.
  2887. go_self.myGM.forEach(_go_wrapper, function(is_wrapperId, io_wrapper) {
  2888. go_self.myGM.forEach(io_wrapper.elements, function(is_elementId, io_element) {
  2889. if(io_element.save) {
  2890. var ls_specificityPrefix = _getSpecificityPrefix(io_element.specificity);
  2891. var lm_oldValue = _go_options[is_wrapperId][is_elementId];
  2892. var lm_newValue = io_element.save(is_wrapperId + is_elementId);
  2893. if(ls_specificityPrefix.length > 0) {
  2894. lm_oldValue = lm_oldValue[ls_specificityPrefix];
  2895. _go_options[is_wrapperId][is_elementId][ls_specificityPrefix] = lm_newValue;
  2896. } else {
  2897. _go_options[is_wrapperId][is_elementId] = lm_newValue;
  2898. }
  2899. if(lm_newValue != lm_oldValue && io_element.changeCallback) {
  2900. setTimeout(function() { io_element.changeCallback(lm_newValue, lm_oldValue); }, 0);
  2901. }
  2902. }
  2903. });
  2904. });
  2905. _saveOptions();
  2906. };
  2907. /**
  2908. * Initializes the options tab for the script and adds the scroll function to the tab menu.
  2909. *
  2910. * @private
  2911. * @inner
  2912. *
  2913. * @return {Promise}
  2914. * A Promise which resolves to the options tab for the script.
  2915. */
  2916. var _initializeOptionsTab = async function() {
  2917. var re_tabScriptOptions = go_self.myGM.$('#tab_options' + go_self.myGM.prefix);
  2918. if(!re_tabScriptOptions) {
  2919. go_self.myGM.addStyle(
  2920. "#tab_options" + go_self.myGM.prefix + " hr { margin: 0; } \
  2921. #tab_options" + go_self.myGM.prefix + " .scriptTextArea { resize: none; width: calc(100% - 2px); height: 75px; } \
  2922. #tab_options" + go_self.myGM.prefix + " .scriptTextField { width: 173px; } \
  2923. #tab_options" + go_self.myGM.prefix + " .cbWrapper { margin: 0 0 0 10px; } \
  2924. #tab_options" + go_self.myGM.prefix + " .radioWrapper:not(:last-child) { margin-bottom: 2px; }",
  2925. 'scriptOptionsTab', true);
  2926. var le_tabmenu = go_self.myGM.$('#tabMenu');
  2927. var le_nextPageLink = go_self.myGM.$('.tabNextPage', le_tabmenu);
  2928. var la_pagerInformation = le_nextPageLink.getAttribute('onclick').match(/switchPage\(([0-9]*), this, ([0-9]*)\)/);
  2929. var li_pageNumber = go_self.Ikariam.getInt(la_pagerInformation[1]);
  2930. var li_offset = go_self.Ikariam.getInt(la_pagerInformation[2]);
  2931. var li_newIndex = go_self.myGM.$$('.tab[index]', le_tabmenu).length + 1;
  2932. var li_newPageNumber = Math.ceil(li_newIndex / li_offset);
  2933. if(li_pageNumber < li_newPageNumber)
  2934. le_nextPageLink.classList.remove('invisible');
  2935. var la_tabClasses = ['tab'];
  2936. if(li_pageNumber !== li_newPageNumber)
  2937. la_tabClasses.push('invisible');
  2938. go_self.myGM.addElement('li', le_tabmenu, {
  2939. 'id': 'js_tab_options' + go_self.myGM.prefix,
  2940. 'classes': la_tabClasses,
  2941. 'index': li_newIndex,
  2942. 'innerHTML': '<b class="tab_options' + go_self.myGM.prefix + '">' + go_script.name + '</b>',
  2943. 'onclick': "$('#js_tab_options" + go_self.myGM.prefix + "').removeClass('selected'); switchTab('tab_options" + go_self.myGM.prefix + "');"
  2944. }, false, le_nextPageLink);
  2945. re_tabScriptOptions = go_self.myGM.addElement('div', go_self.myGM.$('#tabMenu').parentNode, {
  2946. 'id': 'tab_options' + go_self.myGM.prefix,
  2947. 'style': [['display', 'none']]
  2948. }, false);
  2949. }
  2950. _go_optionWrapperVisibility = await go_self.myGM.getValue('optionPanel_optionWrapperVisibility', _go_optionWrapperVisibility);
  2951. return re_tabScriptOptions;
  2952. };
  2953. /**
  2954. * Add a wrapper for options elements to the script option tab.
  2955. *
  2956. * @private
  2957. * @inner
  2958. *
  2959. * @param {Element} ie_tab
  2960. * The tab to add the wrapper to.
  2961. * @param {String} is_id
  2962. * The id of the wrapper.
  2963. * @param {String} is_headerText
  2964. * The text for the wrapper header.
  2965. *
  2966. * @return {Element}
  2967. * The wrapper.
  2968. */
  2969. var _createOptionsWrapper = function(ie_tab, is_id, is_headerText) {
  2970. /*
  2971. * Function to toggle the visibility of an wrapper.
  2972. */
  2973. var lf_toggle = async function() {
  2974. go_self.myGM.toggleShowHideButton(this);
  2975. go_self.myGM.$('.content', this.parentNode.parentNode).classList.toggle('invisible');
  2976. var ls_optionId = this.parentNode.parentNode.id.replace(go_self.myGM.prefix, '');
  2977. _go_optionWrapperVisibility[ls_optionId] = !_go_optionWrapperVisibility[ls_optionId];
  2978. await go_self.myGM.setValue('optionPanel_optionWrapperVisibility', _go_optionWrapperVisibility);
  2979. // Adjust the size of the Scrollbar.
  2980. go_self.ika.controller.adjustSizes();
  2981. };
  2982. var ls_headerText = typeof is_headerText == 'string' ? is_headerText : go_self.Language.$(is_headerText.id);
  2983. var lb_showContent = !!_go_optionWrapperVisibility[is_id];
  2984. var le_optionsWrapper = go_self.myGM.addElement('div', ie_tab, {'id': is_id, 'class': 'contentBox01h' });
  2985. var le_optionsHeader = go_self.myGM.addElement('h3', le_optionsWrapper, { 'class': 'header', 'innerHTML': ls_headerText });
  2986. go_self.myGM.addElement('div', le_optionsHeader, {
  2987. 'class': lb_showContent ? 'minimizeImg' : 'maximizeImg',
  2988. 'style': [['cssFloat', 'left']],
  2989. 'title': lb_showContent ? go_self.Language.$('general.fold') : go_self.Language.$('general.expand'),
  2990. 'click': lf_toggle
  2991. });
  2992. var re_optionsWrapperContent = go_self.myGM.addElement('div', le_optionsWrapper, { 'classes': lb_showContent ? ['content'] : ['content', 'invisible'] });
  2993. go_self.myGM.addElement('div', le_optionsWrapper, { 'class': 'footer' });
  2994. return re_optionsWrapperContent;
  2995. };
  2996. /**
  2997. * Show the option script tab.
  2998. *
  2999. * @private
  3000. * @inner
  3001. *
  3002. * @return {Promise}
  3003. * A Promise which resolves once the option panel is shown.
  3004. */
  3005. var _showOptionPanel = async function() {
  3006. var le_tab = await _initializeOptionsTab();
  3007. for(var i = 0; i < _ga_wrapperOrder.length; i++) {
  3008. var ls_wrapperId = _ga_wrapperOrder[i];
  3009. var lo_wrapperOptions = _go_wrapper[ls_wrapperId];
  3010. var le_wrapper = _createOptionsWrapper(le_tab, ls_wrapperId, lo_wrapperOptions.headerText);
  3011. var lo_tables = {};
  3012. for(var j = 0; j < lo_wrapperOptions.elementOrder.length; j++) {
  3013. var ls_elementId = lo_wrapperOptions.elementOrder[j];
  3014. var lo_elementOptions = lo_wrapperOptions.elements[ls_elementId];
  3015. if(!lo_tables[lo_elementOptions.table]) {
  3016. var le_table = go_self.myGM.addElement('table', le_wrapper, { 'classes': ['moduleContent', 'table01'] });
  3017. lo_tables[lo_elementOptions.table] = go_self.myGM.addElement('tbody', le_table);
  3018. }
  3019. var ls_specificityPrefix = _getSpecificityPrefix(lo_elementOptions.specificity);
  3020. var lo_options = lo_elementOptions.options ? lo_elementOptions.options : null;
  3021. var lm_value = (_go_options[ls_wrapperId] && (_go_options[ls_wrapperId][ls_elementId] || _go_options[ls_wrapperId][ls_elementId] == false)) ? _go_options[ls_wrapperId][ls_elementId] : null;
  3022. if(ls_specificityPrefix.length > 0)
  3023. lm_value = lm_value[ls_specificityPrefix];
  3024. lo_elementOptions.create(lo_tables[lo_elementOptions.table], ls_wrapperId + ls_elementId, lm_value, lo_options);
  3025. }
  3026. go_self.myGM.addButton(le_wrapper, go_self.Language.$('core.optionPanel.save'), function() { setTimeout(_savePanelOptions, 0); });
  3027. }
  3028. };
  3029. /**
  3030. * Show the notification for exporting the options.
  3031. *
  3032. * @private
  3033. * @inner
  3034. */
  3035. var _exportOptionsShowNotification = function() {
  3036. var ls_options = JSON.stringify(_go_options);
  3037. var lo_notificationText = {
  3038. header: go_self.Language.$('core.optionPanel.section.optionPanelOptions.label.exportNotification.header'),
  3039. bodyTop: go_self.Language.$('core.optionPanel.section.optionPanelOptions.label.exportNotification.explanation'),
  3040. bodyBottom: ls_options
  3041. };
  3042. go_self.myGM.notification(lo_notificationText, null, { textarea: true, readonly: true, autoselect: true });
  3043. };
  3044. /**
  3045. * Callback for importing the options.
  3046. *
  3047. * @private
  3048. * @inner
  3049. *
  3050. * @param {Element} ie_textarea
  3051. * The textarea with the options string to import.
  3052. */
  3053. var _importOptionsCallback = function(ie_textarea) {
  3054. var ls_options = ie_textarea.value;
  3055. if(ls_options) {
  3056. // Function for safer parsing.
  3057. var lf_safeParse = function(is_key, im_value) {
  3058. if(typeof im_value === 'function' || Object.prototype.toString.apply(im_value) === '[object function]') {
  3059. return im_value.toString();
  3060. }
  3061. return im_value;
  3062. };
  3063. try {
  3064. var lo_parsed = JSON.parse(ls_options, lf_safeParse);
  3065. // Store the values in the script.
  3066. go_self.myGM.forEach(lo_parsed, function(is_wrapperKey, io_elements) {
  3067. go_self.myGM.forEach(io_elements, function(is_elementKey, im_setting) {
  3068. if(_go_options[is_wrapperKey] && (_go_options[is_wrapperKey][is_elementKey] || _go_options[is_wrapperKey][is_elementKey] == false) && Array.isArray(im_setting) === false) {
  3069. if(typeof im_setting !== 'object') {
  3070. _go_options[is_wrapperKey][is_elementKey] = im_setting;
  3071. } else if(_getSpecificityPrefix(_go_wrapper[is_wrapperKey].elements[is_elementKey].specificity).length > 0) {
  3072. go_self.myGM.forEach(im_setting, function(is_serverKey, im_serverSetting) {
  3073. if(Array.isArray(im_serverSetting) === false && typeof im_serverSetting !== 'object')
  3074. _go_options[is_wrapperKey][is_elementKey] = im_setting;
  3075. });
  3076. }
  3077. }
  3078. });
  3079. });
  3080. _saveOptions();
  3081. } catch(lo_error) {
  3082. var lo_notificationText = {
  3083. header: go_self.Language.$('core.optionPanel.section.optionPanelOptions.label.importError.header'),
  3084. body: go_self.Language.$('core.optionPanel.section.optionPanelOptions.label.importError.explanation')
  3085. };
  3086. go_self.con.error(lo_error);
  3087. go_self.myGM.notification(lo_notificationText);
  3088. }
  3089. }
  3090. };
  3091. /**
  3092. * Show the notification for importing the options.
  3093. *
  3094. * @private
  3095. * @inner
  3096. */
  3097. var _importOptionsShowNotification = function() {
  3098. var lo_notificationText = {
  3099. header: go_self.Language.$('core.optionPanel.section.optionPanelOptions.label.importNotification.header'),
  3100. bodyTop: go_self.Language.$('core.optionPanel.section.optionPanelOptions.label.importNotification.explanation')
  3101. };
  3102. var lo_notificationCallback = {
  3103. confirm: _importOptionsCallback
  3104. };
  3105. go_self.myGM.notification(lo_notificationText, lo_notificationCallback, { textarea: true });
  3106. };
  3107. /**
  3108. * Callback for resetting the options.
  3109. *
  3110. * @private
  3111. * @inner
  3112. */
  3113. var _resetOptionsCallback = function() {
  3114. _go_options = {};
  3115. // Store the default values.
  3116. go_self.myGM.forEach(_go_wrapper, function(is_wrapperKey, io_wrapper) {
  3117. _go_options[is_wrapperKey] = {};
  3118. go_self.myGM.forEach(io_wrapper.elements, function(is_elementKey, io_element) {
  3119. if(io_element.defaultValue || io_element.defaultValue == false) {
  3120. var ls_specificityPrefix = _getSpecificityPrefix(io_element.specificity);
  3121. if(ls_specificityPrefix.length > 0) {
  3122. _go_options[is_wrapperKey][is_elementKey] = {};
  3123. _go_options[is_wrapperKey][is_elementKey][ls_specificityPrefix] = io_element.defaultValue;
  3124. } else {
  3125. _go_options[is_wrapperKey][is_elementKey] = io_element.defaultValue;
  3126. }
  3127. }
  3128. });
  3129. });
  3130. _saveOptions();
  3131. };
  3132. /**
  3133. * Show the notification for resetting the options.
  3134. *
  3135. * @private
  3136. * @inner
  3137. */
  3138. var _resetOptionsShowNotification = function() {
  3139. var lo_notificationText = {
  3140. header: go_self.Language.$('core.optionPanel.section.optionPanelOptions.label.resetNotification.header'),
  3141. body: go_self.Language.$('core.optionPanel.section.optionPanelOptions.label.resetNotification.explanation')
  3142. };
  3143. var lo_notificationCallback = {
  3144. confirm: _resetOptionsCallback,
  3145. abort: function() { return; }
  3146. };
  3147. go_self.myGM.notification(lo_notificationText, lo_notificationCallback);
  3148. };
  3149. /**
  3150. * Create the export options link.
  3151. *
  3152. * @private
  3153. * @inner
  3154. *
  3155. * @param {Element} ie_parent
  3156. * Parent element for the link.
  3157. */
  3158. var _exportOptions = function(ie_parent) {
  3159. this.myGM.addElement('a', ie_parent, {
  3160. 'href': 'javascript:;',
  3161. 'innerHTML': this.Language.$('core.optionPanel.section.optionPanelOptions.label.export'),
  3162. 'click': _exportOptionsShowNotification
  3163. });
  3164. };
  3165. /**
  3166. * Create the import options link.
  3167. *
  3168. * @private
  3169. * @inner
  3170. *
  3171. * @param {Element} ie_parent
  3172. * Parent element for the link.
  3173. */
  3174. var _importOptions = function(ie_parent) {
  3175. this.myGM.addElement('a', ie_parent, {
  3176. 'href': 'javascript:;',
  3177. 'innerHTML': this.Language.$('core.optionPanel.section.optionPanelOptions.label.import'),
  3178. 'click': _importOptionsShowNotification
  3179. });
  3180. };
  3181. /**
  3182. * Create the reset options link.
  3183. *
  3184. * @private
  3185. * @inner
  3186. *
  3187. * @param {Element} ie_parent
  3188. * Parent element for the link.
  3189. */
  3190. var _resetOptions = function(ie_parent) {
  3191. this.myGM.addElement('a', ie_parent, {
  3192. 'href': 'javascript:;',
  3193. 'innerHTML': this.Language.$('core.optionPanel.section.optionPanelOptions.label.reset'),
  3194. 'click': _resetOptionsShowNotification
  3195. });
  3196. };
  3197. /*-------------------------------------------*
  3198. * Public variables, functions and settings. *
  3199. *-------------------------------------------*/
  3200. /**
  3201. * Enum for the level of specificity an option can have.
  3202. *
  3203. * @instance
  3204. * @readonly
  3205. * @name SpecificityLevel
  3206. * @memberof IkariamCore~Options
  3207. *
  3208. * @enum {IkariamCore~Options~SpecificityLevelEnum}
  3209. */
  3210. Object.defineProperty(this, 'SpecificityLevel', { get: function() {
  3211. return _gec_SpecificityLevel;
  3212. } });
  3213. /**
  3214. * Add a wrapper to the list.
  3215. *
  3216. * @instance
  3217. *
  3218. * @param {String} is_id
  3219. * The id of the wrapper.
  3220. * @param {String} is_headerText
  3221. * The text for the wrapper header.
  3222. * @param {int} ii_position
  3223. * The position of the wrapper on the options tab. (optional)
  3224. */
  3225. this.addWrapper = function(is_id, is_headerText, ii_position) {
  3226. if(_go_wrapper[is_id]) {
  3227. go_self.con.warn('Options.addWrapper: Wrapper with id "' + is_id + '" defined two times.');
  3228. } else {
  3229. _go_wrapper[is_id] = { headerText: is_headerText, elements: {}, elementOrder: new Array() };
  3230. _go_options[is_id] = {};
  3231. _ga_wrapperOrder.IC.insert(is_id, ii_position);
  3232. }
  3233. };
  3234. /**
  3235. * Add a new checkbox to the options tab.
  3236. *
  3237. * @instance
  3238. *
  3239. * @param {String} is_id
  3240. * The id of the checkbox.
  3241. * @param {String} is_wrapperId
  3242. * The id of the wrapper.
  3243. * @param {(String|int)} im_block
  3244. * The block of the wrapper, the checkbox belongs to.
  3245. * @param {boolean} ib_defaultChecked
  3246. * If the checkbox is checked by default.
  3247. * @param {String} im_label
  3248. * The text for the label.
  3249. * @param {IkariamCore~Options~DefaultElementOptions} io_options
  3250. * Options for the checkbox.
  3251. */
  3252. this.addCheckbox = function(is_id, is_wrapperId, im_block, ib_defaultChecked, is_label, io_options) {
  3253. /*
  3254. * Function to save the checkbox value.
  3255. */
  3256. var lf_save = function(is_elementId) {
  3257. return go_self.myGM.$('#' + go_self.myGM.prefix + is_elementId + 'Cb').checked;
  3258. };
  3259. /*
  3260. * Function to create the checkbox.
  3261. */
  3262. var lf_create = function(ie_parentTable, is_elementId, ib_value, io_createOptions) {
  3263. var le_row = go_self.myGM.addElement('tr', ie_parentTable);
  3264. var le_parent = go_self.myGM.addElement('td', le_row, { 'colSpan': '2', 'class': 'left' });
  3265. go_self.Ikariam.addCheckboxes(le_parent, [{ id: is_elementId, label: io_createOptions.label, checked: ib_value }]);
  3266. };
  3267. var lo_options = {
  3268. createOptions: { label: is_label },
  3269. defaultValue: ib_defaultChecked,
  3270. specificity: io_options.specificity,
  3271. saveCallback: lf_save,
  3272. changeCallback: io_options.changeCallback,
  3273. position: io_options.position,
  3274. replace: io_options.replace
  3275. };
  3276. _addElement('checkbox', is_id, is_wrapperId, im_block, lf_create, lo_options);
  3277. };
  3278. /**
  3279. * Add a new set of radio buttons to the options tab.
  3280. *
  3281. * @instance
  3282. *
  3283. * @param {String} is_id
  3284. * The id of the checkbox.
  3285. * @param {String} is_wrapperId
  3286. * The id of the wrapper.
  3287. * @param {(String|int)} im_block
  3288. * The block of the wrapper, the checkbox belongs to.
  3289. * @param {(String|int)} im_defaultChecked
  3290. * The value selected by default.
  3291. * @param {String} is_label
  3292. * The text for the label.<br>
  3293. * @param {IkariamCore~myGM~ValueAndLabel} im_radioValues
  3294. * An array with the names an values of the options.
  3295. * @param {IkariamCore~Options~DefaultElementOptions} io_options
  3296. * Options for the radio buttons.
  3297. */
  3298. this.addRadios = function(is_id, is_wrapperId, im_block, im_defaultChecked, is_label, im_radioValues, io_options) {
  3299. /*
  3300. * Function to save the radiobutton value.
  3301. */
  3302. var lf_save = function(is_elementId) {
  3303. return go_self.myGM.getRadioValue(is_elementId);
  3304. };
  3305. /*
  3306. * Function to create the radiobuttons.
  3307. */
  3308. var lf_create = function(ie_parentTable, is_elementId, im_value, io_createOptions) {
  3309. go_self.Ikariam.addRadios(ie_parentTable, is_elementId, im_value, io_createOptions.options, io_createOptions.label);
  3310. };
  3311. var lo_options = {
  3312. createOptions: { label: is_label, options: im_radioValues },
  3313. defaultValue: im_defaultChecked,
  3314. specificity: io_options.specificity,
  3315. saveCallback: lf_save,
  3316. changeCallback: io_options.changeCallback,
  3317. position: io_options.position,
  3318. replace: io_options.replace
  3319. };
  3320. _addElement('radio', is_id, is_wrapperId, im_block, lf_create, lo_options);
  3321. };
  3322. /**
  3323. * Add a new select field to the options tab.
  3324. *
  3325. * @instance
  3326. *
  3327. * @param {String} is_id
  3328. * The id of the select field.
  3329. * @param {String} is_wrapperId
  3330. * The id of the wrapper.
  3331. * @param {(String|int)} im_block
  3332. * The block of the wrapper, the select field belongs to.
  3333. * @param {(String|int)} im_defaultSelected
  3334. * The value of the option selected by default.
  3335. * @param {String} is_label
  3336. * The text for the label.
  3337. * @param {IkariamCore~myGM~ValueAndLabel} im_selectOptions
  3338. * An array with the labels and values of the options.
  3339. * @param {IkariamCore~Options~DefaultElementOptions} io_options
  3340. * Options for the select field.
  3341. */
  3342. this.addSelect = function(is_id, is_wrapperId, im_block, im_defaultSelected, is_label, im_selectOptions, io_options) {
  3343. /*
  3344. * Function to save the select value.
  3345. */
  3346. var lf_save = function(is_elementId) {
  3347. return go_self.myGM.getSelectValue(is_elementId);
  3348. };
  3349. /*
  3350. * Function to create the select.
  3351. */
  3352. var lf_create = function(ie_parentTable, is_elementId, im_value, io_createOptions) {
  3353. go_self.Ikariam.addSelect(ie_parentTable, is_elementId, im_value, io_createOptions.options, io_createOptions.label);
  3354. };
  3355. var lo_options = {
  3356. createOptions: { label: is_label, options: im_selectOptions },
  3357. defaultValue: im_defaultSelected,
  3358. specificity: io_options.specificity,
  3359. saveCallback: lf_save,
  3360. changeCallback: io_options.changeCallback,
  3361. position: io_options.position,
  3362. replace: io_options.replace
  3363. };
  3364. _addElement('select', is_id, is_wrapperId, im_block, lf_create, lo_options);
  3365. };
  3366. /**
  3367. * Add a new textfield to the options tab.
  3368. *
  3369. * @instance
  3370. *
  3371. * @param {String} is_id
  3372. * The id of the textfield.
  3373. * @param {String} is_wrapperId
  3374. * The id of the wrapper.
  3375. * @param {(String|int)} im_block
  3376. * The block of the wrapper, the textfield belongs to.
  3377. * @param {String} is_defaultValue
  3378. * Default value of the textfield.
  3379. * @param {String} is_label
  3380. * The text for the label.
  3381. * @param {IkariamCore~Options~TextFieldOptions} io_options
  3382. * Options for the textfield.
  3383. */
  3384. this.addTextField = function(is_id, is_wrapperId, im_block, is_defaultValue, is_label, io_options) {
  3385. /*
  3386. * Function to save the textfield value.
  3387. */
  3388. var lf_save = function(is_elementId) {
  3389. return go_self.myGM.$('#' + go_self.myGM.prefix + is_elementId + 'TextField').value;
  3390. };
  3391. /*
  3392. * Function to create the textfield.
  3393. */
  3394. var lf_create = function(ie_parentTable, is_elementId, is_value, io_createOptions) {
  3395. var le_row = go_self.myGM.addElement('tr', ie_parentTable);
  3396. var le_labelCell = go_self.myGM.addElement('td', le_row);
  3397. var le_textFieldCell = go_self.myGM.addElement('td', le_row, { 'class': 'left' });
  3398. var ls_label = typeof io_createOptions.label == 'string' ? io_createOptions.label : go_self.Language.$(io_createOptions.label.id);
  3399.  
  3400. go_self.myGM.addElement('span', le_labelCell, { 'innerHTML': ls_label });
  3401. var lo_options = {
  3402. 'id': is_elementId + 'TextField',
  3403. 'classes': ['textfield', 'scriptTextField'],
  3404. 'type': 'text',
  3405. 'value': is_value
  3406. };
  3407. if(!!io_createOptions.maxlength === true)
  3408. lo_options['maxLength'] = io_createOptions.maxLength + '';
  3409. if(!!io_createOptions.style === true)
  3410. lo_options['style'] = io_createOptions.style;
  3411. go_self.myGM.addElement('input', le_textFieldCell, lo_options);
  3412. };
  3413. var lo_options = {
  3414. createOptions: { label: is_label, maxLength: io_options.maxLength, style: io_options.style },
  3415. defaultValue: is_defaultValue,
  3416. specificity: io_options.specificity,
  3417. saveCallback: lf_save,
  3418. changeCallback: io_options.changeCallback,
  3419. position: io_options.position,
  3420. replace: io_options.replace
  3421. };
  3422. _addElement('textfield', is_id, is_wrapperId, im_block, lf_create, lo_options);
  3423. };
  3424. /**
  3425. * Add a new textarea to the options tab.
  3426. *
  3427. * @instance
  3428. *
  3429. * @param {String} is_id
  3430. * The id of the textarea.
  3431. * @param {String} is_wrapperId
  3432. * The id of the wrapper.
  3433. * @param {(String|int)} im_block
  3434. * The block of the wrapper, the textarea belongs to.
  3435. * @param {String} is_defaultValue
  3436. * Default value of the textarea.
  3437. * @param {String} is_label
  3438. * The text for the label.
  3439. * @param {IkariamCore~Options~TextAreaOptions} io_options
  3440. * Options for the textarea.
  3441. */
  3442. this.addTextArea = function(is_id, is_wrapperId, im_block, is_defaultValue, is_label, io_options) {
  3443. /*
  3444. * Function to save the textarea value.
  3445. */
  3446. var lf_save = function(ls_elementId) {
  3447. return go_self.myGM.$('#' + go_self.myGM.prefix + ls_elementId + 'TextArea').value;
  3448. };
  3449. /*
  3450. * Function to create the textarea.
  3451. */
  3452. var lf_create = function(ie_parentTable, is_elementId, is_value, io_createOptions) {
  3453. var ls_label = typeof io_createOptions.label == 'string' ? io_createOptions.label : go_self.Language.$(io_createOptions.label.id);
  3454. var le_labelRow = go_self.myGM.addElement('tr', ie_parentTable);
  3455. var le_labelCell = go_self.myGM.addElement('td', le_labelRow, { 'colSpan': '2', 'class': 'left' });
  3456. go_self.myGM.addElement('p', le_labelCell, { 'innerHTML': ls_label });
  3457. var le_textAreaRow = go_self.myGM.addElement('tr', ie_parentTable);
  3458. var le_textAreaCell = go_self.myGM.addElement('td', le_textAreaRow, { 'colSpan': '2', 'class': 'left' });
  3459. var lo_options = {
  3460. 'id': is_elementId + 'TextArea',
  3461. 'classes': ['textfield', 'scriptTextArea'],
  3462. 'innerHTML': is_value
  3463. };
  3464. if(!!io_createOptions.style === true)
  3465. lo_options['style'] = io_createOptions.style;
  3466. go_self.myGM.addElement('textarea', le_textAreaCell, lo_options);
  3467. };
  3468. var lo_options = {
  3469. createOptions: { label: is_label, style: io_options.style },
  3470. defaultValue: is_defaultValue,
  3471. specificity: io_options.specificity,
  3472. saveCallback: lf_save,
  3473. changeCallback: io_options.changeCallback,
  3474. position: io_options.position,
  3475. replace: io_options.replace
  3476. };
  3477. _addElement('textarea', is_id, is_wrapperId, im_block, lf_create, lo_options);
  3478. };
  3479. /**
  3480. * Add HTML content to the options tab.
  3481. *
  3482. * @instance
  3483. *
  3484. * @param {String} is_id
  3485. * The id of the HTML content.
  3486. * @param {String} is_wrapperId
  3487. * The id of the wrapper.
  3488. * @param {(String|int)} im_block
  3489. * The block of the wrapper, the HTML content belongs to.
  3490. * @param {IkariamCore~Options~HtmlOptions} io_options
  3491. * Options for the html code.
  3492. */
  3493. this.addHTML = function(is_id, is_wrapperId, im_block, io_options) {
  3494. /*
  3495. * Function to create the html.
  3496. */
  3497. var lf_create = function(ie_parentTable, is_elementId, im_value, io_createOptions) {
  3498. var le_htmlRow = go_self.myGM.addElement('tr', ie_parentTable);
  3499. var lo_options = {
  3500. 'colSpan': '2',
  3501. 'class': 'center'
  3502. };
  3503. if(!!io_createOptions.html === true)
  3504. lo_options['innerHTML'] = io_createOptions.html;
  3505. var le_htmlCell = go_self.myGM.addElement('td', le_htmlRow, lo_options);
  3506. if(!!io_createOptions.callback === true)
  3507. io_createOptions.callback.call(io_createOptions.thisReference, le_htmlCell);
  3508. };
  3509. var lo_options = {
  3510. createOptions: { html: io_options.html, callback: io_options.callback, thisReference: io_options.thisReference },
  3511. position: io_options.position,
  3512. replace: io_options.replace
  3513. };
  3514. _addElement('html', is_id, is_wrapperId, im_block, lf_create, lo_options);
  3515. };
  3516. /**
  3517. * Add a new horizontal line to the options tab.
  3518. *
  3519. * @instance
  3520. *
  3521. * @param {String} is_wrapperId
  3522. * The id of the wrapper.
  3523. * @param {(String|int)} im_block
  3524. * The block of the wrapper, the horizontal line belongs to.
  3525. * @param {int} ii_position
  3526. * The position of the horizontal line in the wrapper. (optional)
  3527. *
  3528. * @return {String}
  3529. * The id of the horizontal line.
  3530. */
  3531. this.addLine = function(is_wrapperId, im_block, ii_position) {
  3532. /*
  3533. * Function to create the horizontal line.
  3534. */
  3535. var lf_create = function(ie_parentTable, is_elementId, im_value, io_options) {
  3536. var le_row = go_self.myGM.addElement('tr', ie_parentTable);
  3537. var le_lineCell = go_self.myGM.addElement('td', le_row, { 'colSpan': '2', 'class': 'left' });
  3538. go_self.myGM.addElement('hr', le_lineCell);
  3539. };
  3540. var rs_id = 'hr' + _gi_lineId;
  3541. var lo_options = {
  3542. position: ii_position
  3543. };
  3544. _addElement('line', rs_id, is_wrapperId, im_block, lf_create, lo_options);
  3545. _gi_lineId++;
  3546. return rs_id;
  3547. };
  3548. /**
  3549. * Deletes an wrapper with all option elements contained in it.
  3550. *
  3551. * @instance
  3552. *
  3553. * @param {String} is_id
  3554. * Id of the wrapper to delete.
  3555. */
  3556. this.deleteWrapper = function(is_id) {
  3557. if(!_go_wrapper[is_id]) {
  3558. go_self.con.info('Options.deleteWrapper: Wrapper with id "' + is_id + '" does not exist.');
  3559. } else {
  3560. delete _go_wrapper[is_id];
  3561. delete _go_options[is_id];
  3562. var li_position = -1;
  3563. for(var i = 0; i < _ga_wrapperOrder.length; i++) {
  3564. if(_ga_wrapperOrder[i] == is_id) {
  3565. li_position = i;
  3566. break;
  3567. }
  3568. }
  3569. _ga_wrapperOrder.IC.remove(li_position);
  3570. }
  3571. };
  3572. /**
  3573. * Deletes an option element.
  3574. *
  3575. * @instance
  3576. *
  3577. * @param {String} is_wrapperId
  3578. * The id of the wrapper containing the element.
  3579. * @param {String} is_elementId
  3580. * The id of the element to delete.
  3581. */
  3582. this.deleteElement = function(is_wrapperId, is_elementId) {
  3583. if(!(_go_wrapper[is_wrapperId] && _go_wrapper[is_wrapperId].elements[is_elementId])) {
  3584. go_self.con.info('Options.deleteElement: Element with id "' + is_wrapperId + '_' + is_elementId + '" does not exist.');
  3585. } else {
  3586. delete _go_wrapper[is_wrapperId].elements[is_elementId];
  3587. delete _go_options[is_wrapperId][is_elementId];
  3588. var li_position = -1;
  3589. for(var i = 0; i < _go_wrapper[is_wrapperId].elementOrder.length; i++) {
  3590. if(_go_wrapper[is_wrapperId].elementOrder[i] == is_elementId) {
  3591. li_position = i;
  3592. break;
  3593. }
  3594. }
  3595. _go_wrapper[is_wrapperId].elementOrder.IC.remove(li_position);
  3596. }
  3597. };
  3598. /**
  3599. * Get the stored value of an option.
  3600. *
  3601. * @instance
  3602. *
  3603. * @param {String} is_wrapperId
  3604. * Id of the wrapper of the option element.
  3605. * @param {String} is_optionId
  3606. * Id of the option element.
  3607. *
  3608. * @return {(String|int|boolean)}
  3609. * The stored value.
  3610. */
  3611. this.getOption = function(is_wrapperId, is_optionId) {
  3612. var ls_specificityPrefix = '';
  3613. if(_go_wrapper[is_wrapperId] && _go_wrapper[is_wrapperId].elements[is_optionId])
  3614. ls_specificityPrefix = _getSpecificityPrefix(_go_wrapper[is_wrapperId].elements[is_optionId].specificity);
  3615. if(_go_options[is_wrapperId] && (_go_options[is_wrapperId][is_optionId] || _go_options[is_wrapperId][is_optionId] == false)) {
  3616. if(ls_specificityPrefix.length > 0)
  3617. return _go_options[is_wrapperId][is_optionId][ls_specificityPrefix];
  3618. return _go_options[is_wrapperId][is_optionId];
  3619. }
  3620. go_self.con.warn('Options.getOption: Option with id "' + is_wrapperId + '_' + is_optionId + '" not defined.');
  3621. return null;
  3622. };
  3623. /**
  3624. * Set the stored value of an option.
  3625. *
  3626. * @instance
  3627. *
  3628. * @param {String} is_wrapperId
  3629. * Id of the wrapper of the option element.
  3630. * @param {String} is_optionId
  3631. * Id of the option element.
  3632. * @param {(String|int|boolean)} im_value
  3633. * The value to store.
  3634. */
  3635. this.setOption = function(is_wrapperId, is_optionId, im_value) {
  3636. var ls_specificityPrefix = _getSpecificityPrefix(_go_wrapper[is_wrapperId].elements[is_optionId].specificity);
  3637. if(_go_options[is_wrapperId] && (_go_options[is_wrapperId][is_optionId] || _go_options[is_wrapperId][is_optionId] == false)) {
  3638. if(ls_specificityPrefix.length > 0)
  3639. _go_options[is_wrapperId][is_optionId][ls_specificityPrefix] = im_value;
  3640. else
  3641. _go_options[is_wrapperId][is_optionId] = im_value;
  3642. } else {
  3643. go_self.con.warn('Options.setOption: Option with id "' + is_wrapperId + '_' + is_optionId + '" not yet defined. Value "' + im_value + '" not stored.');
  3644. }
  3645. _saveOptions(true);
  3646. };
  3647. /*----------------------------------------*
  3648. * Register the show option panel handler *
  3649. *----------------------------------------*/
  3650. // Register the option handler to show the options in the option panel.
  3651. go_self.RefreshHandler.add(['options', 'optionsAccount', 'optionsNotification', 'optionsIPSharing'], 'showOptionPanel', _showOptionPanel);
  3652. /*-------------------------------*
  3653. * Add the option panel options. *
  3654. *-------------------------------*/
  3655. this.addWrapper('optionPanelOptions', { id: 'core.optionPanel.section.optionPanelOptions.title' });
  3656. this.addHTML('exportOptions', 'optionPanelOptions', 'links', { thisReference: go_self, callback: _exportOptions });
  3657. this.addHTML('importOptions', 'optionPanelOptions', 'links', { thisReference: go_self, callback: _importOptions });
  3658. this.addHTML('resetOptions', 'optionPanelOptions', 'links', { thisReference: go_self, callback: _resetOptions });
  3659. /*---------------------------------------------------------------------*
  3660. * Types for documentation purposes (e.g. callback functions, objects) *
  3661. *---------------------------------------------------------------------*/
  3662. /**
  3663. * Callback to get the value of an option from the element.
  3664. *
  3665. * @callback IkariamCore~Options~GetOption
  3666. *
  3667. * @private
  3668. * @inner
  3669. *
  3670. * @param {String} elementId
  3671. * The id of the element which option should be retrieved.
  3672. *
  3673. * @return {(String|int|boolean)}
  3674. * The value of the option.
  3675. */
  3676. /**
  3677. * Options for the generic <code>_addElement</code> function.
  3678. *
  3679. * @typedef IkariamCore~Options~AddElementOptions
  3680. *
  3681. * @private
  3682. * @inner
  3683. *
  3684. * @mixes IkariamCore~Options~DefaultElementOptions
  3685. *
  3686. * @property {?*} [createOptions] - Options to pass to the create element function.
  3687. * @property {?(String|int|boolean)} [defaultValue] - Default value of the option. Needed to enable loading of stored option!
  3688. * @property {?IkariamCore~Options~GetOption} [saveCallback] - Callback to get the value of an option from the element.
  3689. */
  3690. /**
  3691. * Callback to create an option element.
  3692. *
  3693. * @callback IkariamCore~Options~CreateCallback
  3694. *
  3695. * @private
  3696. * @inner
  3697. *
  3698. * @param {Element} parentTable
  3699. * The parent table of the option element.
  3700. * @param {String} elementId
  3701. * The id of the option element.
  3702. * @param {(String|int|boolean)} value
  3703. * The value of the option element.
  3704. * @param {*} options
  3705. * Options needed to create this option element.
  3706. */
  3707. /**
  3708. * Callback if the value of an option is changed.
  3709. *
  3710. * @callback IkariamCore~Options~ChangeCallback
  3711. *
  3712. * @param {(String|int|boolean)} newValue
  3713. * The new value of the option.
  3714. * @param {(String|int|boolean)} oldValue
  3715. * The old value of the option.
  3716. */
  3717. /**
  3718. * Default options for elements.
  3719. *
  3720. * @typedef {Object} IkariamCore~Options~DefaultElementOptions
  3721. *
  3722. * @property {?int} [specificity=IkariamCore.SpecificityLevel.GLOBAL] - If the option should be stored globally or for for each server / player specific. Not changable during replacement! Possible values: {@link IkariamCore~Options~SpecificityLevelEnum}
  3723. * @property {?IkariamCore~Options~ChangeCallback} [changeCallback] - Callback if the value of an option is changed.
  3724. * @property {?int} [position=array.length] - Position of the element in the element array. Not changable during replacement!
  3725. * @property {?boolean} [replace=false] - Replace the element with the same name if it has the same type.
  3726. */
  3727. /**
  3728. * Options for text fields.
  3729. *
  3730. * @typedef {Object} IkariamCore~Options~TextFieldOptions
  3731. *
  3732. * @mixes IkariamCore~Options~DefaultElementOptions
  3733. *
  3734. * @property {?int} [maxLength] - The maximum length of the input text.
  3735. * @property {?IkariamCore~myGM~CssStyles} [style] - Special styles to be applied to the element.
  3736. */
  3737. /**
  3738. * Options for text areas.
  3739. *
  3740. * @typedef {Object} IkariamCore~Options~TextAreaOptions
  3741. *
  3742. * @mixes IkariamCore~Options~DefaultElementOptions
  3743. *
  3744. * @property {?IkariamCore~myGM~CssStyles} [style] - Special styles to be applied to the element.
  3745. */
  3746. /**
  3747. * Callback to run after setting the HTML string. Can also be used to create the HTML content.
  3748. *
  3749. * @callback IkariamCore~Options~HtmlCreateCallback
  3750. *
  3751. * @param {Element} parent
  3752. * The parent element of the custom html.
  3753. */
  3754. /**
  3755. * Options for custom html.
  3756. *
  3757. * @typedef {Object} IkariamCore~Options~HtmlOptions
  3758. *
  3759. * @property {?String} [html] - HTML string to add to the wrapper.
  3760. * @property {?IkariamCore~Options~HtmlCreateCallback} [callback] - Callback to run after setting the HTML string. Can also be used to create the HTML content.
  3761. * @property {?*} [thisReference] - Reference to an object which should be referenced by <code>this</code> in the callback as it is not possible to use some objects. (e.g. go_self)
  3762. * @property {?int} [position=array.length] - Position of the element in the element array. Not changable during replacement!
  3763. * @property {?boolean} [replace=false] - Replace the element with the same name if it has the same type.
  3764. */
  3765. /**
  3766. * Enum for the level of specificity an option can have.
  3767. *
  3768. * @typedef {Enum} IkariamCore~Options~SpecificityLevelEnum
  3769. *
  3770. * @property {int} GLOBAL - option is globally set.
  3771. * @property {int} SERVER - option is set for each server specifically.
  3772. * @property {int} PLAYER - option is set for each player specifically.
  3773. */
  3774. }
  3775. /**
  3776. * Handler for options the user can set / change.
  3777. *
  3778. * @instance
  3779. *
  3780. * @type IkariamCore~Options
  3781. */
  3782. this.Options = new Options();
  3783. this.con.logTimeStamp('IkariamCore.Options created');
  3784. /**
  3785. * Instantiate a new set of updating functions and start an initial update check.
  3786. *
  3787. * @inner
  3788. *
  3789. * @class
  3790. * @classdesc Functions for checking for updates for the script.
  3791. */
  3792. function Updater() {
  3793. /*--------------------------------------------*
  3794. * Private variables, functions and settings. *
  3795. *--------------------------------------------*/
  3796. /**
  3797. * Stores if the update check was started by the user.
  3798. *
  3799. * @private
  3800. * @inner
  3801. *
  3802. * @default false
  3803. *
  3804. * @type boolean
  3805. */
  3806. var _gb_manualUpdate = false;
  3807. /**
  3808. * Types for entries in update history. Translations have to be provided as translation
  3809. * in <code>core.update.possible.type.typeName</code><br>
  3810. * Default values which are always set:<br>
  3811. * "release" => release date<br>
  3812. * "other" => entries which type is unknown
  3813. *
  3814. * @private
  3815. * @inner
  3816. *
  3817. * @default ['feature', 'change', 'bugfix', 'language', 'core']
  3818. *
  3819. * @type Array.<String>
  3820. */
  3821. var _ga_updateHistoryEntryTypes = ['feature', 'change', 'bugfix', 'language', 'core'];
  3822. /**
  3823. * Compares two versions and returns if there is a new version.
  3824. *
  3825. * @private
  3826. * @inner
  3827. *
  3828. * @param {String} is_versionOld
  3829. * The old version number.
  3830. * @param {String} is_versionNew
  3831. * The new version number.
  3832. * @param {?int} [ii_maxPartsToCompare=infinite]
  3833. * The number of parts to compare at most.
  3834. *
  3835. * @return {boolean}
  3836. * If a new version is available.
  3837. */
  3838. var _newerVersion = function(is_versionOld, is_versionNew, ii_maxPartsToCompare) {
  3839. var rb_newVersion = false;
  3840. is_versionOld += '';
  3841. is_versionNew += '';
  3842. var la_versionOldParts = is_versionOld.split('.');
  3843. var la_versionNewParts = is_versionNew.split('.');
  3844. var li_biggerNumberOfParts = la_versionOldParts.length > la_versionNewParts.length ? la_versionOldParts.length : la_versionNewParts.length;
  3845. if(!ii_maxPartsToCompare || ii_maxPartsToCompare < 1) {
  3846. ii_maxPartsToCompare = li_biggerNumberOfParts + 1;
  3847. }
  3848. for(var i = 0; i < li_biggerNumberOfParts; i++) {
  3849. var li_versionPartOld = parseInt(la_versionOldParts[i] || 0);
  3850. var li_versionPartNew = parseInt(la_versionNewParts[i] || 0);
  3851. if(li_versionPartOld < li_versionPartNew) {
  3852. rb_newVersion = true;
  3853. break;
  3854. } else if(li_versionPartOld > li_versionPartNew || i == ii_maxPartsToCompare - 1) {
  3855. rb_newVersion = false;
  3856. break;
  3857. }
  3858. }
  3859. return rb_newVersion;
  3860. };
  3861. /**
  3862. * Extract the update history from the metadata.
  3863. *
  3864. * @private
  3865. * @inner
  3866. *
  3867. * @param {Object.<String, Array.<String>>} io_metadata
  3868. * Array with the formatted metadata.
  3869. *
  3870. * @return {Object.<String, Object.<String, Array.<String>>>}
  3871. * The extracted update history.<br>
  3872. * Structure: <code>{ &lt;version&gt;: { &lt;type&gt;: [ &lt;notes&gt; ] }}</code>
  3873. */
  3874. var _extractUpdateHistory = function(io_metadata) {
  3875. var ro_updateHistory = {};
  3876. for(var i = 0; i < io_metadata['history'].length; i++) {
  3877. // Get the information from the update history data.
  3878. var la_history_entry = io_metadata['history'][i].match(/^(\S+)\s+(\S+)\s+(.*)$/);
  3879. var ls_version = la_history_entry[1];
  3880. var ls_type = la_history_entry[2];
  3881. var ls_type_trimmed = ls_type.IC.trim(':').toLowerCase();
  3882. var ls_info = la_history_entry[3];
  3883. if(!ro_updateHistory[ls_version]) {
  3884. ro_updateHistory[ls_version] = {};
  3885. }
  3886. if(ls_type_trimmed == 'release') {
  3887. ro_updateHistory[ls_version]['release'] = ls_info;
  3888. } else if(_ga_updateHistoryEntryTypes.indexOf(ls_type_trimmed) > -1) {
  3889. if(!ro_updateHistory[ls_version][ls_type_trimmed]) {
  3890. ro_updateHistory[ls_version][ls_type_trimmed] = new Array(ls_info);
  3891. } else {
  3892. ro_updateHistory[ls_version][ls_type_trimmed].push(ls_info);
  3893. }
  3894. } else {
  3895. if(!ro_updateHistory[ls_version]['other']) {
  3896. ro_updateHistory[ls_version]['other'] = new Array(ls_type + " " + ls_info);
  3897. } else {
  3898. ro_updateHistory[ls_version]['other'].push(ls_type + " " + ls_info);
  3899. }
  3900. }
  3901. }
  3902. // Return the update history.
  3903. return ro_updateHistory;
  3904. };
  3905. /**
  3906. * Format the update history using some HTML codes.
  3907. *
  3908. * @private
  3909. * @inner
  3910. *
  3911. * @param {Object.<String, Object.<String, Array.<String>>>} io_updateHistory
  3912. * The update history.<br>
  3913. * Structure: <code>{ &lt;version&gt;: { &lt;type&gt;: [ &lt;notes&gt; ] }}</code>
  3914. *
  3915. * @return {String}
  3916. * The formated update history.
  3917. */
  3918. var _formatUpdateHistory = function(io_updateHistory) {
  3919. var rs_formattedUpdateHistory = '';
  3920. for(var ls_version in io_updateHistory) {
  3921. if(Object.prototype.hasOwnProperty.call(io_updateHistory, ls_version)) {
  3922. rs_formattedUpdateHistory += '<h2>v' + ls_version + '</h2><span class="smallFont">' + io_updateHistory[ls_version]['release'] + '</span></small><br><table class="' + go_self.myGM.prefix + 'updateTable"><tbody>';
  3923. for(var ls_type in io_updateHistory[ls_version]) {
  3924. if(Object.prototype.hasOwnProperty.call(io_updateHistory[ls_version], ls_type) && ls_type != 'release') {
  3925. rs_formattedUpdateHistory += '<tr><td class="' + go_self.myGM.prefix + 'updateDataType">' + go_self.Language.$('core.update.possible.type.' + ls_type) + '</td><td class="' + go_self.myGM.prefix + 'updateDataInfo"><ul>';
  3926. for(var i = 0 ; i < io_updateHistory[ls_version][ls_type].length; i++) {
  3927. rs_formattedUpdateHistory += '<li>' + io_updateHistory[ls_version][ls_type][i] + '</li>';
  3928. }
  3929. rs_formattedUpdateHistory += '</ul></td></tr>';
  3930. }
  3931. }
  3932. rs_formattedUpdateHistory += '</tbody></table><br>';
  3933. }
  3934. }
  3935. if(rs_formattedUpdateHistory.length === 0) {
  3936. rs_formattedUpdateHistory = '<b>' + go_self.Language.$('core.update.possible.noHistory') + '</b>';
  3937. } else {
  3938. rs_formattedUpdateHistory = '<b><u>' + go_self.Language.$('core.update.possible.history') + '</u></b><br><br>' + rs_formattedUpdateHistory;
  3939. }
  3940. return rs_formattedUpdateHistory;
  3941. };
  3942. /**
  3943. * Show the update information panel.
  3944. *
  3945. * @private
  3946. * @inner
  3947. *
  3948. * @param {Object.<String, Array.<String>>} io_metadata
  3949. * Array with formatted metadata.
  3950. */
  3951. var _showUpdateInfo = function(io_metadata) {
  3952. var lo_updateHistory = _extractUpdateHistory(io_metadata);
  3953. var lo_notificationText = {
  3954. header: go_self.Language.$('core.update.possible.header'),
  3955. bodyTop: go_self.Language.$('core.update.possible.text', ['<a href="https://greatest.deepsurf.us/scripts/' + go_script.id + '" target="_blank" >' + go_script.name + '</a>', go_script.version, io_metadata.version]) + '<br>&nbsp;',
  3956. bodyBottom: _formatUpdateHistory(lo_updateHistory),
  3957. confirm: go_self.Language.$('core.update.possible.button.install'),
  3958. abort: go_self.Language.$('core.update.possible.button.hide')
  3959. };
  3960. var lo_notificationCallback = {
  3961. confirm: function() { go_self.win.top.location.href = 'https://greatest.deepsurf.us/scripts/' + go_script.id + '/code/' + go_script.id + '.user.js'; },
  3962. abort: function() { go_self.myGM.setValue('updater_hideUpdate', io_metadata.version + ''); }
  3963. };
  3964. go_self.myGM.notification(lo_notificationText, lo_notificationCallback);
  3965. };
  3966. /**
  3967. * Format the given metadata.
  3968. *
  3969. * @private
  3970. * @inner
  3971. *
  3972. * @param {String} is_metadata
  3973. * The metadata to format.
  3974. *
  3975. * @return {Object.<String, Array.<String>>}
  3976. * The formatted metadata.
  3977. */
  3978. var _formatMetadata = function(is_metadata) {
  3979. var rs_metadata = new Array();
  3980. // Extract the tags from the metadata.
  3981. var ls_innerMetadata = is_metadata.match(/\/\/ ==UserScript==((.|\n|\r)*?)\/\/ ==\/UserScript==/)[0];
  3982. if(ls_innerMetadata) {
  3983. // Extract all tags.
  3984. var la_metadata_entries = ls_innerMetadata.match(/\/\/ @(.*?)(\n|\r)/g);
  3985. for(var i = 0; i < la_metadata_entries.length; i++) {
  3986. // Extract the data from the tag.
  3987. var la_metadata_entry = la_metadata_entries[i].match(/\/\/ @(.*?)\s+(.*)/);
  3988. if(!rs_metadata[la_metadata_entry[1]]) {
  3989. rs_metadata[la_metadata_entry[1]] = new Array(la_metadata_entry[2]);
  3990. } else {
  3991. rs_metadata[la_metadata_entry[1]].push(la_metadata_entry[2]);
  3992. }
  3993. }
  3994. }
  3995. return rs_metadata;
  3996. };
  3997. /*-------------------------------------------*
  3998. * Public variables, functions and settings. *
  3999. *-------------------------------------------*/
  4000. /**
  4001. * Check for updates for the script. Automatically done on every instantiation of {@link IkariamCore}
  4002. * if the period from the last update is bigger than the check interval.
  4003. *
  4004. * @instance
  4005. *
  4006. * @return {Promise}
  4007. * A Promise which resolves once the update check url is called.
  4008. */
  4009. this.checkForUpdates = async function() {
  4010. // Send a request to the script hosting server to get the metadata of the script to check if there is a new update.
  4011. var lb_notPossible = await go_self.myGM.xhr({
  4012. method: 'GET',
  4013. url: 'https://greatest.deepsurf.us/scripts/' + go_script.id + '/code.meta.js',
  4014. headers: {'User-agent': 'Mozilla/5.0', 'Accept': 'text/html'},
  4015. onload: async function(io_response) {
  4016. var lo_metadata = _formatMetadata(io_response.responseText);
  4017. if(_newerVersion(go_script.version, lo_metadata.version, go_self.Options.getOption('updateOptions', 'updateNotifyLevel')) && (await go_self.myGM.getValue('updater_hideUpdate', go_script.version) != lo_metadata.version || _gb_manualUpdate)) {
  4018. _showUpdateInfo(lo_metadata);
  4019. } else if(_gb_manualUpdate) {
  4020. var lo_notificationText = {
  4021. header: go_self.Language.$('core.update.noNewExists.header'),
  4022. body: go_self.Language.$('core.update.noNewExists.text', ['<a href="https://greatest.deepsurf.us/scripts/' + go_script.id + '" target="_blank" >' + go_script.name + '</a>', go_script.version])
  4023. };
  4024. go_self.myGM.notification(lo_notificationText);
  4025. }
  4026. }
  4027. });
  4028. if(lb_notPossible && lb_notPossible == true) {
  4029. go_self.Options.setOption('updateOptions', 'updateInterval', 2419200);
  4030.  
  4031. var lo_notificationText = {
  4032. header: go_self.Language.$('core.update.notPossible.header'),
  4033. body: go_self.Language.$('core.update.notPossible.text', ['<a href="https://greatest.deepsurf.us/scripts/' + go_script.id + '" target="_blank" >' + go_script.name + '</a>', go_script.version])
  4034. };
  4035.  
  4036. go_self.myGM.notification(lo_notificationText);
  4037. }
  4038. };
  4039. /**
  4040. * Search manually for updates. Forces to search for updates. Even shows a popup if no new update is available.
  4041. *
  4042. * @instance
  4043. */
  4044. this.doManualUpdate = function() {
  4045. _gb_manualUpdate = true;
  4046. go_self.myGM.setValue('updater_lastUpdateCheck', (new Date()).getTime() + '').then(function() { go_self.Updater.checkForUpdates(); });
  4047. };
  4048. /**
  4049. * Set the possible entries for update history entries. "release" for the release date and "other"
  4050. * for all entries which are not known will be stipped as they are default. Translations have to be
  4051. * provided as translation in <code>core.update.possible.type.typeName</code>
  4052. *
  4053. * @instance
  4054. *
  4055. * @param {Array.<String>} ia_updateHistoryEntryTypes
  4056. * The array with the update history entries to set.
  4057. */
  4058. this.setUpdateHistoryEntryTypes = function(ia_updateHistoryEntryTypes) {
  4059. ['release', 'other'].forEach(function(is_toStrip) {
  4060. var li_index = ia_updateHistoryEntryTypes.indexOf('release');
  4061. if(li_index !== -1)
  4062. ia_updateHistoryEntryTypes.IC.remove(li_index);
  4063. });
  4064. _ga_updateHistoryEntryTypes = ia_updateHistoryEntryTypes;
  4065. };
  4066. /*------------------------*
  4067. * Add the updater styles *
  4068. *------------------------*/
  4069. go_self.myGM.addStyle(
  4070. "." + go_self.myGM.prefix + "updateTable { border-collapse: separate; border-spacing: 2px; } \
  4071. ." + go_self.myGM.prefix + "updateDataType { width: 100px; padding: 5px 0px 5px 5px; border: 1px solid #D2A860; } \
  4072. ." + go_self.myGM.prefix + "updateDataInfo { width: 300px; padding: 5px 5px 5px 20px; border: 1px solid #D2A860; } \
  4073. ." + go_self.myGM.prefix + "updateDataInfo ul li { list-style: disc outside none; }",
  4074. 'updater', true
  4075. );
  4076. /*----------------------*
  4077. * Register the options *
  4078. *----------------------*/
  4079. var _ga_updateIntervalOpts = new Array(
  4080. { value: -1, label: { id: 'core.optionPanel.section.update.label.interval.option.never' } },
  4081. { value: 3600, label: { id: 'core.optionPanel.section.update.label.interval.option.hour' } },
  4082. { value: 43200, label: { id: 'core.optionPanel.section.update.label.interval.option.hour12' } },
  4083. { value: 86400, label: { id: 'core.optionPanel.section.update.label.interval.option.day' } },
  4084. { value: 259200, label: { id: 'core.optionPanel.section.update.label.interval.option.day3' } },
  4085. { value: 604800, label: { id: 'core.optionPanel.section.update.label.interval.option.week' } },
  4086. { value: 1209600, label: { id: 'core.optionPanel.section.update.label.interval.option.week2' } },
  4087. { value: 2419200, label: { id: 'core.optionPanel.section.update.label.interval.option.week4' } }
  4088. );
  4089. var _ga_updateNotifyLevelOpts = new Array(
  4090. { value: 0, label: { id: 'core.optionPanel.section.update.label.notifyLevel.option.all' } },
  4091. { value: 1, label: { id: 'core.optionPanel.section.update.label.notifyLevel.option.major' } },
  4092. { value: 2, label: { id: 'core.optionPanel.section.update.label.notifyLevel.option.minor' } },
  4093. { value: 3, label: { id: 'core.optionPanel.section.update.label.notifyLevel.option.patch' } }
  4094. );
  4095. var _searchUpdates = function(ie_parent) {
  4096. var ls_updateLink = this.Language.$('core.optionPanel.section.update.label.manual', [go_script.name]);
  4097. this.myGM.addElement('a', ie_parent, { 'href': 'javascript:;', 'innerHTML': ls_updateLink, 'click': go_self.Updater.doManualUpdate });
  4098. };
  4099. go_self.Options.addWrapper('updateOptions', { id: 'core.optionPanel.section.update.title' }, 1);
  4100. go_self.Options.addSelect('updateInterval', 'updateOptions', 'generalOptions', 3600, { id: 'core.optionPanel.section.update.label.interval.description' }, _ga_updateIntervalOpts, {});
  4101. go_self.Options.addSelect('updateNotifyLevel', 'updateOptions', 'generalOptions', 0, { id: 'core.optionPanel.section.update.label.notifyLevel.description' }, _ga_updateNotifyLevelOpts, {});
  4102. go_self.Options.addHTML('manualUpdateLink', 'updateOptions', 'manualUpdate', { thisReference: go_self, callback: _searchUpdates });
  4103. /*-------------------------------------*
  4104. * Check automatically for new updates *
  4105. *-------------------------------------*/
  4106. setTimeout(async function() {
  4107. var li_lastCheck = await go_self.myGM.getValue('updater_lastUpdateCheck', 0);
  4108. var li_millis = (new Date()).getTime();
  4109. var li_diff = li_millis - li_lastCheck;
  4110. var li_interval = go_self.Options.getOption('updateOptions', 'updateInterval') * 1000;
  4111. // Greasyfork allows updatechecks only every 24 hours.
  4112. li_interval = li_interval < 86400 ? 86400 : li_interval;
  4113. if(li_interval > 0 && li_diff > li_interval) {
  4114. _gb_manualUpdate = false;
  4115.  
  4116. go_self.myGM.setValue('updater_lastUpdateCheck', li_millis + '').then(function () { go_self.Updater.checkForUpdates(); });
  4117. }
  4118. }, 0);
  4119. }
  4120. /**
  4121. * Updater to check for updates.
  4122. *
  4123. * @instance
  4124. *
  4125. * @type IkariamCore~Updater
  4126. */
  4127. this.Updater = new Updater();
  4128. this.con.logTimeStamp('IkariamCore.Updater created');
  4129. this.con.logTimeStamp('IkariamCore display error functions initiated');
  4130. this.con.groupEnd();
  4131. }