Ikariam Core

Framework for Ikariam userscript developers.

目前为 2016-01-27 提交的版本。查看 最新版本

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.gf.qytechs.cn/scripts/5574/103830/Ikariam%20Core.js

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

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址