Ikariam Core

//@SCRIPT_DESCRIPTION_DEFAULT@v

当前为 2017-06-18 提交的版本,查看 最新版本

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/5574/201396/Ikariam%20Core.js

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

QingJ © 2025

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