// ==UserScript== // @name Enhanced UI // @description Enhancements for the user interface of Ikariam. // @namespace Tobbe // @author Tobbe // @version 2.5 // // @include http://s*.*.ikariam.*/* // @include http://s*.ikariam.gameforge.com/* // // @exclude http://support.*.ikariam.gameforge.com/* // // @resource languageGerman http://resources.ikascripts.de/74221/v2.3/languageGerman.json // @resource languageEnglish http://resources.ikascripts.de/74221/v2.3/languageEnglish.json // @resource languageLatvian http://resources.ikascripts.de/74221/v2.3/languageLatvian.json // // @grant unsafeWindow // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_listValues // @grant GM_getResourceText // @grant GM_xmlhttpRequest // // @history 2.5 Bugfix: Game language is recognized again. // @history 2.5 Bugfix: Works now in Greasemonkey 2.0+ // @history 2.5 Checks now for updates on Greasy Fork. // @history 2.5 Removed code parts for mobile version. // @history 2.5 Removed alliance page improvement, as fixed in Ikariam v0.5.13 // // @history 2.4 New Ikariam domain -> script can now be used with this domain // // @history 2.3 Feature: Formatting troop lists for posting in forums / personal messages. (desktop). // @history 2.3 Feature: Filling level of warehouse as bar in town view. (desktop). // @history 2.3 Feature: Link to mines / town hall when clicking on resources / citizens. (desktop). // @history 2.3 Feature: New versioning system. // @history 2.3 Bugfix: Some characters in links were not decoded correctly. (desktop) // @history 2.3 Bugfix: Watching a foreign city causes a abortion of the script. (desktop) // @history 2.3 Bugfix: The city dropdown sometimes was zoomed in world view. (desktop) // @history 2.3 Bugfix: Removed obsolete CSS styles for Firefox. // @history 2.3 Adjustments in the language files. // @history 2.3 Violent Monkey brings Greasemonkey functions to Opera. // // @history 2.2.1 Feature: Smaller icons in direct military tooltip. (desktop) // @history 2.2.1 Bugfix: Problems with update check and version numbers >= 10. (mobile & desktop) // @history 2.2.1 Bugfix: Problems with another script. (desktop) // @history 2.2.1 Bugfix: Problems with a wrong style in island view. (desktop) // // @history 2.2 Feature: Message signature can be set. (desktop) // @history 2.2 Feature: Button for faster sending of circular messages. (desktop) // @history 2.2 Feature: Make links in messages clickable. (desktop) // @history 2.2 Feature: Show town infos of colonizing cities. (desktop) // @history 2.2 Feature: Information about cargo / fleets is displayed directly in military view. (desktop) // @history 2.2 Feature: Script options: Sections on the script tab can be folded. (desktop) // @history 2.2 Bugfix: Tooltips with mouseover were not clickable anymore. (desktop) // @history 2.2 Changes in code for better overview. // // @history 2.1.1 Feature: Different styles for income in town view. (desktop) // @history 2.1.1 Feature: Remaining resources after upgrade. (desktop) // @history 2.1.1 Feature: Improved style for external alliance pages. (desktop) // @history 2.1.1 Feature: Refresh the missing / remaining resources in ruction view automatically. (desktop) // // @history 2.1 Feature: Show the missing resources in ruction view. (desktop) // @history 2.1 Feature: Show the hourly income directly in town view and add the daily production as popup. (desktop) // @history 2.1 Feature: Don't center town information in the town advisor. (desktop) // @history 2.1 Feature: Save the highscore data of alliance members and compare it with the actual value. (desktop) // @history 2.1 Bugfix: There was an error with a missing language package and seperators. (mobile & desktop) // @history 2.1 Bugfix: Some things in worldview were not resized correctly. (desktop) // @history 2.1 Prevent more than one script execution. // @history 2.1 Prevent more than one script option panel (the script option panel now is usable for other scripts, too). // // @history 2.0.1 Bugfix: Zooming was broken. // @history 2.0.1 Bugfix: Dropdown menus created by the script were broken. // @history 2.0.1 Bugfix: Tooltips in in Alliance / Military view were not shown correctly. // // @history 2.0 Feature: Cross-browser compatibility. (Firefox, Chrome, Opera) // @history 2.0 Feature: Latvian translation - thanks to Draxo. (mobile & desktop) // @history 2.0 Feature: Possibility to hide the update hint for a new script version. (mobile & desktop) // @history 2.0 Bugfix: Resizing the owner state in world view was broken. (desktop) // @history 2.0 Some changes in the code for easier copying of functions which should be used by more than one script. // // @history 1.7 Feature: Resizing banners when zooming is possible in city view, too. (desktop) // @history 1.7 Feature: The zoom buttons are available in world view, too. (desktop) // @history 1.7 Feature: Zooming with the mouse scroll is possible again (now with key, standard is ctrl). (desktop) // @history 1.7 Feature: Changes in the option panel due to the new zooming function features. (desktop) // @history 1.7 Bugfix: If resizing is enabled, zooming with the buttons will resize the banners, too. (desktop) // @history 1.7 Bugfix: The chat will not cause to much executions of script functions. (desktop) // @history 1.7 The language texts are integrated as resources, so that there is shorter code. // @history 1.7 Replace the GM_* functions by myGM.* to expand them easy and add new. // // @history 1.6 Feature: Possibility to hide only the bird swarm animation. (desktop) // @history 1.6 Feature: Easier upkeep reduction table. (mobile & desktop) // @history 1.6 Feature: Enhanced zoom function using the Ikariam zoom function. (desktop) // @history 1.6 Due to the use of Ikariam functions the code could be reduced. // @history 1.6 Code enhancements for shorter code. // // @history 1.5.1.1 Bugfix: Not all occurrences of hidden were changed. // // @history 1.5.1 Bugfix: Name of a class (hidden) is used by GF. // // @history 1.5 Feature: Options panel to enable/disable funtions and set settings. (mobile & desktop) // @history 1.5 Feature: Update interval can be set. (mobile & desktop) // @history 1.5 Feature: Manually check for updates. (mobile & desktop) // @history 1.5 Feature: Zoom funtion without resizing the whole view. (desktop) // @history 1.5 Feature: Move loading circle to another position. (desktop) // @history 1.5 Feature: Show tooltip in Alliance / Military view on mouseover. (desktop) // @history 1.5 Feature: Code better commented. More comments, so that it is easier to understand. // @history 1.5 Bugfix: Changed *.gif to *.png. // @history 1.5 Version numbers adjusted. // // @history 1.4.1 Feature: Support for mobile interface. // @history 1.4.1 Bugfix: Fixed bug with scrollbar in finances view. (desktop) // // @history 1.4 Feature: Ready for 0.5.0, but also supports 0.4.5 furthermore. // @history 1.4 Feature: Implemented support for different languages. // @history 1.4 Feature: Enhanced script updater. // @history 1.4 Feature: Cleaned up code. // @history 1.4 Feature: Rename the script to "Enhanced UI". // @history 1.4 Feature: Change the namespace to "Tobbe". // @history 1.4 Because of the change of namespace and name you have to delete the old script manually! // // @history 1.3.3 Bugfix: Problem with negative numbers and 0.4.2.4 fixed. // // @history 1.3.2 Feature: Own script updater. // @history 1.3.2 Bugfix: Remove everything what refered to other scripts. // // @history 1.3.1 Feature: New script updater. // // @history 1.3 Remove the script updater (Because of the problem with Greasemonkey scripts). // // @history 1.2.1 Feature: New style of update panel. // @history 1.2.1 Bugfix: Bug with ',' as seperator fixed. // // @history 1.2 Feature: Income in 24h added. // @history 1.2 Feature: Cleaned up code. // // @history 1.1 Feature: Update check implemented. // // @history 1.0 Initial release. // @downloadURL none // ==/UserScript== /****************************************************************************************************************** ******************************************************************************************************************* *** *** *** The update function which is used in the script was developed by PhasmaExMachina and adapted by me (Tobbe). *** *** *** ******************************************************************************************************************* ******************************************************************************************************************/ /** * Information about the Script. */ var scriptInfo = { version: '2.5', id: 4369, name: 'Ikariam Enhanced UI', author: 'Tobbe', debug: false }; /** * Information about the language. */ var languageInfo = { implemented: new Array('English', 'German', 'Latvian'), useResource: true, defaultText: {"default":{"update":{"notPossible":{"header":"No Update possible","text1":"It is not possible to check for updates for ","text2":". Please check manually for Updates for the script. The actual installed version is ","text3":". This message will appear again in four weeks."},"possible":{"header":"Update available","text1":"There is an update for ","text2":" available.","text3":"At the moment there is version ","text4":" installed. The newest version is ","history":"Version History","type":{"feature":"Feature(s)","bugfix":"Bugfix(es)","other":""},"button":{"install":"Install","hide":"Hide"}},"noNewExists":{"header":"No Update available","text1":"There is no new version for ","text2":" available. The newest version ","text3":" is installed."}},"notification":{"header":"Script notification","button":{"confirm":"OK","abort":"Abort"}}},"settings":{"kiloSep":",","decSep":".","left2right":true},"general":{"successful":"Your order has been carried out.","error":"There was an error in your request.","ctrl":"Ctrl","alt":"Alt","shift":"Shift","yes":"Yes","no":"No","show":"Show","hide":"Hide"},"balance":{"income":{"perHour":"Income per hour","perDay":"Income per day","start":"Income without reduction"},"upkeep":{"reason":{"0":"Troops","1":"Ships","2":"Troops & Ships"},"basic":"Basic Costs","supply":"Supply Costs","result":"Total Costs"}},"optionPanel":{"scripts":"Scripts","save":"Save settings!","section":{"module":{"title":"Modules","label":{"updateActive":"Search for updates automatically","incomeOnTopActive":"Show income on top in Balance view","upkeepReductionActive":"Show a short version of the upkeep reduction","missingResActive":"Show missing resources in construction view","resourceInfoActive":"Show the hourly income directly in town view","capacityInfoActive":"Show info bar for warehouse capacity","easyAccessActive":"Link resource number to town hall / mines","zoomActive":"Activate zoom in world view, island view, town view","messageSignatureActive":"Enable message signatures","easyCircularMsgActive":"Show button for faster sending of circular messages","replaceUrlsActive":"Make links in messages clickable","colonizingLinksActive":"Show information about colonizing cities","lcMoveActive":"Move loading circle to position bar","tooltipsAutoActive":"Show tooltips in alliance mebers view and military advisor automatically","directMilitaryTtActive":"Show information about cargo / fleets in military view without tooltips","unitInfoActive":"Show troop info","hideBirdsActive":"Hide the bird swarm.","nctAdvisorActive":"Don't center town information in the town advisor","niceAllyPageActive":"Show the external allypages better formatted","memberInfoActive":"Enable the possibility to save highscore data of alliance members"}},"update":{"title":"Update","label":{"interval":{"description":"Interval to search for updates:","option":{"hour":"1 hour","hour12":"12 hours","day":"1 day","day3":"3 days","week":"1 week","week2":"2 weeks","week4":"4 weeks"}},"manual":{"text1":"Search for updates for ","text2":"!"}}},"resInfoMissingRes":{"title":"Resource Information / Missing Resources","label":{"hourlyIncomeStyle":"Style of the hourly income in town view:","align":{"right":"Right align","left":"Left align","rightWithSeparation":"Right align with separation"},"capacityOrientationStyle":"Orientation of the bar","orientation":{"vertical":"Vertical","horizontal":"Horizontal","horizontalFull":"Horizontal, full length"},"hasBorder":"Has border","showBranchRes":"Show resources in trading post","showPositive":"Show also the remaining resources after an upgrade","showColoured":"Show the remaining resources coloured"}},"zoom":{"title":"Zoom function","label":{"zoomFactor":{"world":"Zoom worldmap:","island":"Zoom island view:","town":"Zoom town view:"},"scaleChildren":{"description":"Let banners and symbols in normal size when zooming when zooming in this view:","world":"Worldmap","island":"Island view","town":"Town view"},"accessKeys":"This keys must be pressed to zoom with the mouse:"}},"messageSignature":{"title":"Message signature","label":{"useServerSignature":"Use local signature","placementTop":"Insert signature above cited messages","signatureText":{"global":"Global signature, which could be used on every world:","server":"Local signature, which only could be used on this world:"}}}}},"memberInfo":{"show":"Alliance info","reset":"Reset","lastReset":"Time since the last reset:","noReset":"No reset so far."},"unitInfo":{"button":"Troop information","header":"Troops in ","units":{"label":"Units in ","own":"Own units","friends":"Allied units","enemies":"Enemy units"},"ships":{"label":"Ships in ","own":"Own ships","friends":"Allied ships","enemies":"Enemy ships"}},"resourceInfo":{"dailyIncome":{"wood":"Daily production building material:","wine":"Daily production wine:","marble":"Daily production marble:","glass":"Daily production crystal glass:","sulfur":"Daily production sulphur:"}},"easyCircularMsg":{"getLink":"Save link for circular message","send":"Send circular message"},"replacedUrl":{"notification":{"header":"Attention!","text1":"You're going to open the link ","text2":". This happens on your own risk. Proceed?"}},"zoomFunction":{"zoomIn":"Zoom in","zoomFactor":"Zoom factor","zoomOut":"Zoom out"},"name":{"resource":{"gold":"Gold","wood":"Building Material","wine":"Wine","marble":"Marble","glass":"Crystal Glass","sulfur":"Sulphur"},"unit":{"swordsman":"Swordsman","phalanx":"Hoplite","archer":"Archer","marksman":"Sulphur Carabineer","mortar":"Mortar","slinger":"Slinger","catapult":"Catapult","ram":"Battering Ram","steamgiant":"Steam Giant","bombardier":"Balloon-Bombardier","cook":"Cook","medic":"Doctor","girocopter":"Gyrocopter","spearman":"Spearman"},"ship":{"ballista":"Ballista Ship","catapult":"Catapult Ship","flamethrower":"Fire Ship","mortar":"Mortar Ship","ram":"Ram Ship","steamboat":"Steam Ram","rocketship":"Rocket Ship","submarine":"Diving Boat","paddlespeedship":"Paddle Speedboat","ballooncarrier":"Balloon Carrier","tender":"Tender","transport":"Merchant Ship"}}} }; /********************************************************** *********************************************************** ***** ***** ***** Start of functions / variables for all Scripts. ***** ***** ***** *********************************************************** **********************************************************/ /** * Sets unsafeWindow / window to win for easier access. */ var win = typeof unsafeWindow != 'undefined' ? unsafeWindow : window; /************************** *** Debugging settings. *** **************************/ // For more information about commands that are available for the Firebug console see http://getfirebug.com/wiki/index.php/Console_API. if(scriptInfo.debug && win.debugConsole) { var conTmp = win.debugConsole; } else { var conTmp = { // Non static functions are set to 'null'. log: function () { return false; }, info: function () { return false; }, warn: function () { return false; }, error: function () { return false; }, debug: function () { return false; }, assert: function () { return false; }, clear: function () { return false; }, dir: function () { return false; }, dirxml: function () { return false; }, trace: function () { return false; }, group: function () { return false; }, groupCollapsed: function () { return false; }, groupEnd: function () { return false; }, time: function () { return false; }, timeEnd: function () { return false; }, profile: function () { return false; }, profileEnd: function () { return false; }, count: function () { return false; }, exception: function () { return false; }, table: function () { return false; } }; } /** * Debugging console. */ var con = conTmp; /******************************** *** Functions for all Strings *** ********************************/ /** * Replaces characters or whitespaces at the beginning of a string. * * @param String toRemove * A string containing the characters to remove (optional, if not set: trim whitespaces). * * @return String * The trimmed string. */ String.prototype.ltrim = function(toRemove) { // Is there a string with the characters to remove? var special = !!toRemove; // Return the trimmed string. return special ? this.replace(new RegExp('^[' + toRemove + ']+'), '') : this.replace(/^\s+/, ''); }; /** * Replaces characters or whitespaces at the end of a string. * * @param String toRemove * A string containing the characters to remove (optional, if not set: trim whitespaces). * * @return String * The trimmed string. */ String.prototype.rtrim = function(toRemove) { // Is there a string with the characters to remove? var special = !!toRemove; // Return the trimmed string. return special ? this.replace(new RegExp('[' + toRemove + ']+$'), '') : this.replace(/\s+$/, ''); }; /** * Replaces characters or whitespaces at the beginning and end of a string. * * @param String toRemove * A string containing the characters to remove (optional, if not set: trim whitespaces). * * @return String * The trimmed string. */ String.prototype.trim = function(toRemove) { // Return the trimmed string. return this.ltrim(toRemove).rtrim(toRemove); }; /** * Encodes HTML-special characters in a string. * * @return String * The encoded string. */ String.prototype.encodeHTML = function() { // Set the characters to encode. var characters = { '&': '&', '"': '"', '\'': ''', '<': '<', '>': '>' }; // Return the encoded string. return this.replace(/([\&"'<>])/g, function(string, symbol) { return characters[symbol]; }); }; /** * Decodes HTML-special characters in a string. * * @return String * The decoded string. */ String.prototype.decodeHTML = function() { // Set the characters to decode. var characters = { '&': '&', '"': '"', ''': '\'', '<': '<', '>': '>' }; // Return the decoded string. return this.replace(/("|'|<|>|&)/g, function(string, symbol) { return characters[symbol]; }); }; /** * Repeats a string a specified number of times. * * @param int nr * The number of times to repeat the string. * * @return String * The repeated string. */ String.prototype.repeat = function(nr) { // Storage for the result. var ret = this; // Repeat the string. for(var i = 1; i < nr; i++) { ret += this; } // Return the string. return ret; }; /******************************** *** myGM / Updater / Language *** ********************************/ /** * myGM for cross-browser compatibility of the GM_* functions. (use myGM.* instead of GM_*) * Also there are general used functions stored. */ myGM = { /** * If it is possible to use the default GM_*Value functions, true, otherwise false. */ canUseGmStorage: false, /** * If it is possible to use the default GM_getResourceText function, true, otherwise false. */ canUseGmRessource: false, /** * If it is possible to use the default GM_xmlhttpRequest function, true, otherwise false. */ canUseGmXhr: false, /** * If it is possible to use the localStorage, true, otherwise false. */ canUseLocalStorage: false, /** * The prefix which schuld be added to the values stored in localStorage / cookies. */ prefix: '', /** * Storage for the style sheets which will be added by the script. */ styleSheets: new Array(), /** * Storage for the notification id. */ notificationId: 0, /** * Init myGM. */ init: function() { // Set the prefix for the script. this.prefix = 'script' + scriptInfo.id; // Set the possibility to use the different storages. this.canUseLocalStorage = !!win.localStorage; this.canUseGmStorage = !(typeof GM_getValue == 'undefined' || (typeof GM_getValue.toString == 'function' && GM_getValue.toString().indexOf('not supported') > -1)) && !(typeof GM_setValue == 'undefined' || (typeof GM_setValue.toString == 'function' && GM_setValue.toString().indexOf('not supported') > -1)) && !(typeof GM_deleteValue == 'undefined' || (typeof GM_deleteValue.toString == 'function' && GM_deleteValue.toString().indexOf('not supported') > -1)) && !(typeof GM_listValues == 'undefined' || (typeof GM_listValues.toString == 'function' && GM_listValues.toString().indexOf('not supported') > -1)); // Set the possibility to use the GM_resource. this.canUseGmRessource = !(typeof GM_getResourceText == 'undefined' || (typeof GM_getResourceText.toString == 'function' && GM_getResourceText.toString().indexOf('not supported') > -1)); // Set the possibillity to use the GM_xhr. this.canUseGmXhr = !(typeof GM_xmlhttpRequest == 'undefined' || (typeof GM_xmlhttpRequest.toString == 'function' && GM_xmlhttpRequest.toString().indexOf('not supported') > -1)); // Set the close image. It is stored as a data URI. If you copy it to your browser, a red cross (16px * 16px) will be shown. var closeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAIhSURBVDjLlZPrThNRFIWJicmJz6BWiYbIkYDEG0JbBiitDQgm0PuFXqSAtKXtpE2hNuoPTXwSnwtExd6w0pl2OtPlrphKLSXhx07OZM769qy19wwAGLhM1ddC184+d18QMzoq3lfsD3LZ7Y3XbE5DL6Atzuyilc5Ciyd7IHVfgNcDYTQ2tvDr5crn6uLSvX+Av2Lk36FFpSVENDe3OxDZu8apO5rROJDLo30+Nlvj5RnTlVNAKs1aCVFr7b4BPn6Cls21AWgEQlz2+Dl1h7IdA+i97A/geP65WhbmrnZZ0GIJpr6OqZqYAd5/gJpKox4Mg7pD2YoC2b0/54rJQuJZdm6Izcgma4TW1WZ0h+y8BfbyJMwBmSxkjw+VObNanp5h/adwGhaTXF4NWbLj9gEONyCmUZmd10pGgf1/vwcgOT3tUQE0DdicwIod2EmSbwsKE1P8QoDkcHPJ5YESjgBJkYQpIEZ2KEB51Y6y3ojvY+P8XEDN7uKS0w0ltA7QGCWHCxSWWpwyaCeLy0BkA7UXyyg8fIzDoWHeBaDN4tQdSvAVdU1Aok+nsNTipIEVnkywo/FHatVkBoIhnFisOBoZxcGtQd4B0GYJNZsDSiAEadUBCkstPtN3Avs2Msa+Dt9XfxoFSNYF/Bh9gP0bOqHLAm2WUF1YQskwrVFYPWkf3h1iXwbvqGfFPSGW9Eah8HSS9fuZDnS32f71m8KFY7xs/QZyu6TH2+2+FAAAAABJRU5ErkJggg%3D%3D'; // Set the notification style. this.addStyle( "." + 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; } \ ." + this.prefix + "notificationPanelContainer { z-index: 1000000000001; position: fixed; visibility: visible; top: 100px; left: 50%; width: 500px; height: 370px; margin-left: -250px; padding: 0; text-align: left; color: #333; font: 12px Arial,Helvetica,sans-serif; } \ ." + this.prefix + "notificationPanel { position: relative; top: 0px; left: 0px; background-color: transparent; border: 0 none; overflow: hidden; } \ ." + this.prefix + "notificationPanelHeader { height: 39px; background: none repeat scroll 0 0 transparent; font-weight: bold; line-height: 2; white-space: nowrap; } \ ." + this.prefix + "notificationPanelHeaderL { } \ ." + this.prefix + "notificationPanelHeaderR { } \ ." + this.prefix + "notificationPanelHeaderM { height: 24px; background-color: #999; padding: 5px; border: 3px solid #000; border-radius: 5px; color: #811709; line-height: 1.34em; font-size: 15pt; } \ ." + this.prefix + "notificationPanelBody { max-height: 311px; height: 100%; background: none repeat scroll 0 0 transparent; } \ ." + this.prefix + "notificationPanelBodyL { } \ ." + this.prefix + "notificationPanelBodyR { } \ ." + this.prefix + "notificationPanelBodyM { height: 100%; background-color: #F5F5F5; border-left: 3px solid #000; border-right: 3px solid #000; border-top-left-radius: 5px; border-top-right-radius: 5px; padding: 0 10px; font-size: 14px; } \ ." + this.prefix + "notificationPanelBodyMTop { max-height: 100px; line-height: 2; } \ ." + this.prefix + "notificationPanelBodyMTop b { line-height: 3.5; font-size: 110%; } \ ." + this.prefix + "notificationPanelBodyM a { color: #811709; font-weight: bold; } \ ." + this.prefix + "notificationPanelBodyM h2 { font-weight: bold; } \ ." + this.prefix + "notificationPanelBodyMContent { max-height: 265px; padding: 10px; background-color: #FFF; border: 1px dotted #4D4D4D; font: 14px Arial,Helvetica,sans-serif; color: #333; border-collapse: separate; overflow-y:auto; margin-top: 5px; } \ ." + this.prefix + "notificationPanelBodyMBottom { max-height: 170px; padding: 10px; background-color: #FFF; border: 1px dotted #4D4D4D; font: 14px Arial,Helvetica,sans-serif; color: #333; border-collapse: separate; overflow-y:auto; } \ ." + this.prefix + "notificationPanelBodyPlaceholder { height: 20px; } \ ." + this.prefix + "notificationPanelFooter { height: 20px; background: none repeat scroll 0 0 transparent; } \ ." + this.prefix + "notificationPanelFooterL { } \ ." + this.prefix + "notificationPanelFooterR { } \ ." + this.prefix + "notificationPanelFooterM { background-color: #F5F5F5; border-bottom: 3px solid #000; border-left: 3px solid #000; border-right: 3px solid #000; border-bottom-left-radius: 5px; border-bottom-right-radius: 5px; padding: 3px 0 2px 3px; font-size: 77%; } \ ." + this.prefix + "notificationPanelClose { cursor: pointer; position: absolute; top: 10px; right: 10px; width: 16px; height: 16px; background-image: url('" + closeImage + "'); } \ ." + this.prefix + "notificationPanelButtonWrapper { bottom: -4px; position: absolute; margin: 10px auto; width: 100%; text-align: center; } \ ." + this.prefix + "notificationPanelButton { background-color: #DADEDE; border-color: #C0C0C0 #4D4D4D #4D4D4D #C0C0C0; border-style: double; border-width: 3px; border-radius: 5px; cursor: pointer; display: inline; font-weight: bold; margin: 0px 5px; padding: 2px 10px; text-align: center; font-size: 12px; width: 100px; } \ ." + this.prefix + "notificationPanelButton:hover { color: #444; } \ ." + this.prefix + "notificationPanelButton:active { border-color: #4D4D4D #C0C0C0 #C0C0C0 #4D4D4D; border-style: double; border-width: 3px; padding: 3px 10px 1px; } \ ." + this.prefix + "notificationPanelButtonConfirm { } \ ." + this.prefix + "notificationPanelButtonAbort { }", 'notification' ); // Set the updater style. this.addStyle( "." + this.prefix + "updateTable { border-collapse: separate; border-spacing: 2px; } \ ." + this.prefix + "updateDataType { width: 100px; padding: 5px 0px 5px 5px; border: 1px solid #999; } \ ." + this.prefix + "updateDataInfo { width: 300px; padding: 5px 5px 5px 20px; border: 1px solid #999; } \ ." + this.prefix + "updateDataInfo ul li { list-style: disc outside none; }", 'updater' ); }, /** * Store a value specified by a key. * * @param String key * The key of the value. * @param mixed value * The value to store. */ setValue: function(key, value) { var toStore = ''; // Stringify the value to store also arrays. toStore = JSON.stringify(value); // If the use of the default GM_setValue ist possible, use it. if(this.canUseGmStorage) { // Store the value. GM_setValue(key, toStore); // Otherwise use the local storage if possible. } else if(this.canUseLocalStorage) { // Store the value. win.localStorage.setItem(this.prefix + key, toStore); // Otherwise use cookies. } else { // Prepare the cookie name and value. var data = escape(this.prefix + key) + '=' + escape(toStore); // Set the expire date to January 1st, 2020. var expire = 'expires=' + (new Date(2020, 0, 1, 0, 0, 0, 0)).toGMTString(); // Set the path to root. var path = 'path=/'; // Made the cookie accessible from all servers. var domain = 'domain=ikariam.com'; // Set the cookie. win.document.cookie = data + ';' + expire + ';' + path + ';' + domain; } }, /** * Get a value and return it. * * @param String key * The key of the value. * @param mixed defaultValue * The value which is set if the value is not set. * * @return mixed * The stored value. */ getValue: function(key, defaultValueOld) { defaultValue = JSON.stringify(defaultValueOld); // Storage for the value. var value = defaultValue; // If the use of the default GM_getValue ist possible, use it. if(this.canUseGmStorage) { // Get the value. value = GM_getValue(key, defaultValue); // Otherwise use the local storage if possible. } else if(this.canUseLocalStorage) { // Get the value. var valueTmp = win.localStorage.getItem(this.prefix + key); // If the value is not set, let the default value set. if(valueTmp) { value = valueTmp; } // Otherwise use cookies. } else { // Get all cookies. var allCookies = document.cookie.split("; "); // Loop over all cookies. for(var i = 0; i < allCookies.length; i++) { // Get the key and value of a cookie. var oneCookie = allCookies[i].split("="); // If the key is the correct key, get the value. if(oneCookie[0] == escape(this.prefix + key)) { // Get the value and abort the loop. value = unescape(oneCookie[1]); break; } } } // Return the value (parsed for the correct return type). return JSON.parse(value); }, /** * Delete a value specified by a key. * * @param String key * The key of the value. */ deleteValue: function(key) { // If the use of the default GM_deleteValue ist possible, use it. if(this.canUseGmStorage) { // Delete the value. GM_deleteValue(key); // Otherwise use the local storage if possible. } else if(this.canUseLocalStorage) { // Remove the value. win.localStorage.removeItem(this.prefix + key); // Otherwise use cookies. } else { // Prepare the cookie name. var data = escape(this.prefix + key) + '='; // Set the expire date to January 1st, 2000 (this will delete the cookie). var expire = 'expires=' + (new Date(2000, 0, 1, 0, 0, 0, 0)).toGMTString(); // Set the path to root. var path = 'path=/'; // Made the cookie accessible from all servers. var domain = 'domain=ikariam.com'; // Set the cookie. win.document.cookie = data + ';' + expire + ';' + path + ';' + domain; } }, /** * Returns an array with the keys of all values stored by the script. * * @return mixed[] * The array with all keys. */ listValues: function() { // Create an array for the storage of the values keys. var key = new Array(); // If the use of the default GM_listValues ist possible, use it. if(this.canUseGmStorage) { // Store the key(s) to the array. key = GM_listValues(); // Otherwise use the local storage if possible. } else if(this.canUseLocalStorage) { // Loop over all stored values. for(var i = 0; i < win.localStorage.length; i++) { // Get the key name of the key with the number i. var keyName = win.localStorage.key(i); // If the value is set by the script, push the key name to the array. if(keyName.indexOf(this.prefix) != -1) { key.push(keyName.replace(this.prefix, '')); } } // Otherwise use cookies. } else { // Get all cookies. var allCookies = document.cookie.split("; "); // Loop over all cookies. for(var i = 0; i < allCookies.length; i++) { // Get the key name of a cookie. var keyName = unescape(allCookies[i].split("=")[0]); // If the key value is set by the script, push the key name to the array. if(keyName.indexOf(this.prefix) != -1) { key.push(keyName.replace(this.prefix, '')); } } } // Return all keys. return key; }, /** * Adds a style element to the head of the page and return it. * * @param String styleRules * The style rules to be set. * @param String id * An id for the style set, to have the possibility to delete it. (optional, if none is set, the stylesheet is not stored) * @param boolean overwrite * If a style with id should overwrite an existing style. * * @return boolean * If the stylesheet was stored with the id. */ addStyle: function(styleRules, id, overwrite) { // If the element was stored is saved here. var storedWithId = false; // If overwrite, remove the old style sheet. if(overwrite && overwrite == true) { this.removeStyle(id); } // If the stylesheet doesn't exists. if(!id || (id && !this.styleSheets[id])) { // Create a new style element and set the style rules. var style = this.addElement('style', document.head); style.type = 'text/css'; style.innerHTML = styleRules; // If an id is set, store it. if(id) { this.styleSheets[id] = style; storedWithId = true; } } // Return if the stylesheet was stored with an id. return storedWithId; }, /** * Removes a style element set by the script. * * @param String id * The id of the stylesheet to delete. * * @return boolean * If the stylesheet could be deleted. */ removeStyle: function(id) { // Stores if the stylesheet could be removed. var removed = false; // If there is an id set and a stylesheet with the id exists. if(id && this.styleSheets[id]) { // Remove the stylesheet from the page. document.head.removeChild(this.styleSheets[id]); // Remove the stylesheet from the array. delete this.styleSheets[id]; // Set removed to true. removed = true; } // Return if the stylesheet could be removed. return removed; }, /** * Returns the content of a resource parsed with JSON.parse. * * @param String name * The name of the resource to parse. */ getResourceParsed: function(name) { // Storage for the response text. var responseText = ''; // Function for safer parsing. var safeParse = function(key, value) { // If the value is a function, return just the string, so it is not executable. if(typeof value === 'function' || Object.prototype.toString.apply(value) === '[object function]') { return value.toString(); } // Return the value. return value; }; // If the use of the default GM_getRessourceText ist possible, use it. if(this.canUseGmRessource) { // Set the parsed text. responseText = GM_getResourceText(name); // Otherwise perform a xmlHttpRequest. } else { // Perform the xmlHttpRequest. responseText = this.xhr({ method: 'GET', url: 'http://resources.ikascripts.de/' + scriptInfo.id + '/v' + scriptInfo.version + '/' + name + '.json', headers: { 'User-agent': navigator.userAgent, 'Accept': 'text/html' }, synchronous: true, onload: function(response) { return false; } }); } return JSON.parse(responseText.trim(), safeParse); }, /** * Makes a cross-site XMLHttpRequest. * * @param mixed[] args * The arguments the request needs. (specified here: http://wiki.greasespot.net/GM_xmlhttpRequest) */ xhr: function(args) { // Storage for the result of the request. var responseText; // Check if all required data is given. if(!args.method || !args.url || !args.onload) { return false; } // If the use of the default GM_xmlhttpRequest ist possible, use it. if(this.canUseGmXhr) { // Sent the request. var response = GM_xmlhttpRequest(args); // Get the response text. responseText = response.responseText; // Otherwise show a hint for the missing possibility to fetch the data. } else { // Storage if the link fetches metadata from userscripts.org var usoUpdate = (args.url.search(/^http:\/\/userscripts.org\/scripts\/source\/[0-9]+\.meta\.js$/i) != -1); var isJSON = (args.url.search(/\.json$/i) != -1); // If it is a metadata request. if(usoUpdate) { // Set the update interval to max. this.setValue('updater_updateInterval', 28 * 24 * 60 * 60); // Set the notification text. var notificationText = { header: Language.$('default_update_notPossible_header'), body: Language.$('default_update_notPossible_text1') + '' + scriptInfo.name + '' + Language.$('default_update_notPossible_text2') + scriptInfo.version + Language.$('default_update_notPossible_text3') }; // Show a notification. this.notification(notificationText); responseText = true; // Otherwise if it is JSON. } else if(isJSON) { // Do the request with a string indicating the error. args.onload('{ "is_error": true }'); // Return a string indicating the error. responseText = '{ "is_error": true }'; // Otherwise. } else { responseText = false; } } // Return the responseText. return responseText; }, /** * Shows a notification to the user. * * Possible notification texts: * text.header (optional) * text.body or text.bodyTop & text.bodyBottom * text.confirm (optional) * text.abort (optional) * * @param String[] text * The notification texts. * @param function[] callback * The callbacks for confirm and abort. (optional, default: close panel) * * @return int * The notification id. */ notification: function(text, callback) { // Get the notification id. this.notificationId += 1; var notificationId = this.notificationId; // Create the background and the container. myGM.addElement('div', document.body, 'notificationBackground' + notificationId, 'notificationBackground', null, true); var notificationPanelContainer = myGM.addElement('div', document.body, 'notificationPanelContainer' + notificationId, 'notificationPanelContainer', null, true); var notificationPanel = myGM.addElement('div', notificationPanelContainer, 'notificationPanel' + notificationId, 'notificationPanel', null, true); // Create the notification panel header. var notificationPanelHeader = myGM.addElement('div', notificationPanel, 'notificationPanelHeader' + notificationId, 'notificationPanelHeader', null, true); var notificationPanelHeaderL = myGM.addElement('div', notificationPanelHeader, 'notificationPanelHeaderL' + notificationId, 'notificationPanelHeaderL', null, true); var notificationPanelHeaderR = myGM.addElement('div', notificationPanelHeaderL, 'notificationPanelHeaderR' + notificationId, 'notificationPanelHeaderR', null, true); var notificationPanelHeaderM = myGM.addElement('div', notificationPanelHeaderR, 'notificationPanelHeaderM' + notificationId, 'notificationPanelHeaderM', null, true); // Create the notification panel body. var notificationPanelBody = myGM.addElement('div', notificationPanel, 'notificationPanelBody' + notificationId, 'notificationPanelBody', null, true); var notificationPanelBodyL = myGM.addElement('div', notificationPanelBody, 'notificationPanelBodyL' + notificationId, 'notificationPanelBodyL', null, true); var notificationPanelBodyR = myGM.addElement('div', notificationPanelBodyL, 'notificationPanelBodyR' + notificationId, 'notificationPanelBodyR', null, true); var notificationPanelBodyM = myGM.addElement('div', notificationPanelBodyR, 'notificationPanelBodyM' + notificationId, 'notificationPanelBodyM', null, true); if(text.body) { var notificationPanelBodyMContent = myGM.addElement('div', notificationPanelBodyM, 'notificationPanelBodyMContent' + notificationId, 'notificationPanelBodyMContent', null, true); } else { var notificationPanelBodyMTop = myGM.addElement('div', notificationPanelBodyM, 'notificationPanelBodyMTop' + notificationId, 'notificationPanelBodyMTop', null, true); var notificationPanelBodyMBottom = myGM.addElement('div', notificationPanelBodyM, 'notificationPanelBodyMBottom' + notificationId, 'notificationPanelBodyMBottom', null, true); } myGM.addElement('div', notificationPanelBodyM, 'notificationPanelBodyPlaceholder' + notificationId, 'notificationPanelBodyPlaceholder', null, true); // Create the notification panel footer. var notificationPanelFooter = myGM.addElement('div', notificationPanel, 'notificationPanelFooter' + notificationId, 'notificationPanelFooter', null, true); var notificationPanelFooterL = myGM.addElement('div', notificationPanelFooter, 'notificationPanelFooterL' + notificationId, 'notificationPanelFooterL', null, true); var notificationPanelFooterR = myGM.addElement('div', notificationPanelFooterL, 'notificationPanelFooterR' + notificationId, 'notificationPanelFooterR', null, true); var notificationPanelFooterM = myGM.addElement('div', notificationPanelFooterR, 'notificationPanelFooterM' + notificationId, 'notificationPanelFooterM', null, true); // Create the button wrapper. var notificationPanelButtonWrapper = myGM.addElement('div', notificationPanel, 'notificationPanelButtonWrapper' + notificationId, 'notificationPanelButtonWrapper', null, true); // Create the confirm button. var notificationPanelConfirm = myGM.addElement('input', notificationPanelButtonWrapper, 'notificationPanelConfirm' + notificationId, new Array('notificationPanelButton', 'notificationPanelButtonConfirm'), null, true); notificationPanelConfirm.type = 'button'; notificationPanelConfirm.value = text.confirm ? text.confirm : Language.$('default_notification_button_confirm'); // Create the abort button if needed. if(callback && callback.abort) { var notificationPanelAbort = myGM.addElement('input', notificationPanelButtonWrapper, 'notificationPanelAbort' + notificationId, new Array('notificationPanelButton', 'notificationPanelButtonAbort'), null, true); notificationPanelAbort.type = 'button'; notificationPanelAbort.value = text.abort ? text.abort : Language.$('default_notification_button_abort'); } // Insert the texts into header, body and footer. notificationPanelHeaderM.innerHTML = (text.header ? text.header : Language.$('default_notification_header')) + '
'; notificationPanelFooterM.innerHTML = scriptInfo.name + ' v' + scriptInfo.version; if(text.body) { notificationPanelBodyMContent.innerHTML = text.body; } else { notificationPanelBodyMTop.innerHTML = text.bodyTop ? text.bodyTop : ''; notificationPanelBodyMBottom.innerHTML = text.bodyBottom ? text.bodyBottom : ''; } // Function to close the notification panel. var closeNotificationPanel = function() { // Remove the notification background. document.body.removeChild(myGM.$('#' + myGM.prefix + 'notificationBackground' + notificationId)); // Remove the notification panel. document.body.removeChild(myGM.$('#' + myGM.prefix + 'notificationPanelContainer' + notificationId)); }; // Add event listener to the buttons to close / install. this.$('#' + this.prefix + 'notificationPanelClose' + notificationId).addEventListener('click', closeNotificationPanel, false); if(callback && callback.confirm) { notificationPanelConfirm.addEventListener('click', function() { closeNotificationPanel(); callback.confirm(); }, false); } else { notificationPanelConfirm.addEventListener('click', closeNotificationPanel, false); } if(callback && callback.abort) { notificationPanelAbort.addEventListener('click', function() { closeNotificationPanel(); callback.abort(); }, false); } return notificationId; }, /** * Gets the first matching child element by a query and returns it. * * @param String query * The query for the element. * @param element parent * The parent element. (optional, default document) * * @return element * The element. */ $: function(query, parent) { return this.$$(query, parent)[0]; }, /** * Gets all matching child elements by a query and returns them. * * @param String query * The query for the elements. * @param element parent * The parent element. (optional, default document) * * @return element[] * The elements. */ $$: function(query, parent) { // If there is no parent set, set it to document. if(!parent) parent = document; // Return the elements. return Array.prototype.slice.call(parent.querySelectorAll(query)); }, /** * Creates a new element and adds it to a parent. * * @param String type * The type of the new element. * @param element parent * The parent of the new element. * @param int id * The last part of the id of the element. (optional, if not set, no id will be set) * @param String || String[] classes * The class(es) of the element. (optional, if not set, no class will be set) * @param mixed[] style * The styles of the element. (optional, if not set, no style will be set) * @param boolean || boolean[] hasPrefix * If no prefix should be used. (optional, if not set, a prefix will be used for id and no prefix will be used for classes) * @param element nextSib * The next sibling of the element. (optional, if not set, the element will be added at the end) * * @return element * The new element. */ addElement: function(type, parent, id, classes, style, hasPrefix, nextSib) { // Create the new Element. var newElement = document.createElement(type); // If there is a id, set it. if(id) { // Get the id prefix. var idPrefix = !(hasPrefix == false || (hasPrefix && hasPrefix.id == false)) ? this.prefix : ''; // Set the id. newElement.id = idPrefix + id; } // Add all classes. if(classes && classes != '') { // Get the class prefix. var classPrefix = !!(hasPrefix == true || (hasPrefix && hasPrefix.classes == true)) ? this.prefix : ''; // Set the class(es). if(typeof classes == 'string') { newElement.classList.add(classPrefix + classes); } else { for(var i = 0; i < classes.length; i++) { newElement.classList.add(classPrefix + classes[i]); } } } if(style) { for(var i = 0; i < style.length; i++) { newElement.style[style[i][0]] = style[i][1]; } } // If there is the next sibling defined, insert it before it. if(nextSib) { parent.insertBefore(newElement, nextSib); // Otherwise insert it at the end. } else { parent.appendChild(newElement); } // Return the new element. return newElement; } }; /** * Functions for updater. */ Updater = { /** * Stores if the update was instructed by the user. */ manualUpdate: false, /** * Init the Updater. */ init: function() { // Get the difference between now and the last check. var lastCheck = myGM.getValue('updater_lastUpdateCheck', 0); var millis = (new Date()).getTime(); var diff = millis - lastCheck; // If the module is active and the last update is enough time before, check for updates. if(myGM.getValue('module_updateActive', true) && diff > myGM.getValue('updater_updateInterval', 3600) * 1000) { // No manual Update. this.manualUpdate = false; // Check for Updates. this.checkForUpdates(); // Set the time for the last update check to now. myGM.setValue('updater_lastUpdateCheck', millis + ''); } }, /** * Search manually for updates. */ doManualUpdate: function() { // Manual Update. Updater.manualUpdate = true; // Check for Updates. Updater.checkForUpdates(); // Set the time for the last update check to now. myGM.setValue('updater_lastUpdateCheck', (new Date()).getTime() + ''); }, /** * Check for updates for the Script. * * @return boolean * If there is a newer version. */ checkForUpdates: function() { // Send a request to the userscripts.org server to get the metadata of the script to check if there is a new Update. myGM.xhr({ method: 'GET', url: 'https://greasyfork.org/scripts/' + scriptInfo.id + '/code.meta.js', headers: {'User-agent': 'Mozilla/5.0', 'Accept': 'text/html'}, onload: function(response) { // Extract the metadata from the response. var metadata = Updater.formatMetadata(response.responseText); // If a new Update is available and the update hint should be shown. if(Updater.newerVersion(scriptInfo.version, metadata.version2) && (myGM.getValue('updater_hideUpdate', scriptInfo.version) != metadata.version2 || Updater.manualUpdate)) { // Show update dialogue. Updater.showUpdateInfo(metadata); // If there is no new update and it was a manual update show hint. } else if(Updater.manualUpdate) { // Set the notification text. var notificationText = { header: Language.$('default_update_noNewExists_header'), body: Language.$('default_update_noNewExists_text1') + '' + scriptInfo.name + '' + Language.$('default_update_noNewExists_text2') + scriptInfo.version + Language.$('default_update_noNewExists_text3') }; // Show a notification. myGM.notification(notificationText); } } }); }, /** * Show the update information panel. * * @param String versionOld * The old version number. * @param String versionNew * The new version number. * @param int maxPartsToCompare * The number of parts to compare at most. (optional, default "compare all parts") * * @return boolean * If a new version is available. */ newerVersion: function(versionOld, versionNew, maxPartsToCompare) { // Stores if a new version is available. var newVersion = false; // Force both versions to be a string. versionOld += ''; versionNew += ''; // The parts of the versions. var versionOldParts = versionOld.split('.'); var versionNewParts = versionNew.split('.'); // The bigger number of parts of the versions. var biggerNumberOfParts = versionOldParts.length > versionNewParts.length ? versionOldParts.length : versionNewParts.length; // If all parts should be compared, set maxPartsToCompare to all parts. if(!maxPartsToCompare || maxPartsToCompare < 1) { maxPartsToCompare = biggerNumberOfParts + 1; } // Loop over all parts of the version with less parts. for(var i = 0; i < biggerNumberOfParts; i++) { // Get the value of the parts. var versionPartOld = parseInt(versionOldParts[i] || 0); var versionPartNew = parseInt(versionNewParts[i] || 0); // If the old part is smaller than the new, return true. if(versionPartOld < versionPartNew) { newVersion = true; break; // Else if the old part is bigger than the new it is now new version; return false. } else if(versionPartOld > versionPartNew || i == maxPartsToCompare - 1) { newVersion = false; break; } } // No new version, return false. return newVersion; }, /** * Show the update information panel. * * @param mixed[] metadata * Array with formated metadata */ showUpdateInfo: function(metadata) { // Get the update history. var updateHistory = this.extractUpdateHistory(metadata); // Set the notification text. var notificationText = { header: Language.$('default_update_possible_header'), bodyTop: Language.$('default_update_possible_text1') + '' + scriptInfo.name + '' + Language.$('default_update_possible_text2') + '.
' + Language.$('default_update_possible_text3') + scriptInfo.version + Language.$('default_update_possible_text4') + metadata.version + '.
  ' + Language.$('default_update_possible_history') + '', bodyBottom: this.formatUpdateHistory(updateHistory), confirm: Language.$('default_update_possible_button_install'), abort: Language.$('default_update_possible_button_hide') }; // Set the notification callback. var notificationCallback = { confirm: function() { win.top.location.href = 'http://userscripts.org/scripts/source/' + scriptInfo.id + '.user.js'; }, abort: function() { myGM.setValue('updater_hideUpdate', metadata.version + ''); } }; // Show a notification. myGM.notification(notificationText, notificationCallback); }, /** * Format the given metadata. * * @param String metadata * The metadata to format. * * @return String[] * The formated metadata as array. */ formatMetadata: function(metadataIn) { // Create an array for the formated metadata. var metadataOut = new Array(); // Extract the tags from the metadata. var innerMeta = metadataIn.match(/\/\/ ==UserScript==((.|\n|\r)*?)\/\/ ==\/UserScript==/)[0]; // If there are some tags. if(innerMeta) { // Extract all tags. var tags = innerMeta.match(/\/\/ @(.*?)(\n|\r)/g); // Loop over all tags. for(var i = 0; i < tags.length; i++) { // Extract the data from the tag. var tmp = tags[i].match(/\/\/ @(.*?)\s+(.*)/); // If there is no data with this tag create a new array to store all data with this tag. if(!metadataOut[tmp[1]]) { metadataOut[tmp[1]] = new Array(tmp[2]); // Otherwise add the data to the existing array. } else { metadataOut[tmp[1]].push(tmp[2]); } } } // Return the formated metadata. return metadataOut; }, /** * Extract the update history from the metadata. * * @param String[] metadata * Array with the formated metadata. * * @return mixed[] * The extracted update history. */ extractUpdateHistory: function(metadata) { // Create variable to store the update history. var updateHistory = new Array(); // Loop over all update history data. for(var i = 0; i < metadata['history'].length; i++) { // Get the information from the update history data. var tmp = metadata['history'][i].match(/^(\S+)\s+(\S+)\s+(.*)$/); // If there is no array for this version create one. if(!updateHistory[tmp[1]]) { updateHistory[tmp[1]] = new Array(); } // If it is a feature store it to feature in this version. if(tmp[2] == 'Feature:') { if(!updateHistory[tmp[1]]['feature']) { updateHistory[tmp[1]]['feature'] = new Array(tmp[3]); } else { updateHistory[tmp[1]]['feature'].push(tmp[3]); } // If it is a bugfix store it to bugfix in this version. } else if(tmp[2] == 'Bugfix:') { if(!updateHistory[tmp[1]]['bugfix']) { updateHistory[tmp[1]]['bugfix'] = new Array(tmp[3]); } else { updateHistory[tmp[1]]['bugfix'].push(tmp[3]); } // Otherwise store it to other in this version. } else { if(!updateHistory[tmp[1]]['other']) { updateHistory[tmp[1]]['other'] = new Array(tmp[2] + " " + tmp[3]); } else { updateHistory[tmp[1]]['other'].push(tmp[2] + " " + tmp[3]); } } } // Return the update history. return updateHistory; }, /** * Format the update history. * * @param mixed[] updateHistory * The update history. * * @return String * The formated update history. */ formatUpdateHistory: function(updateHistory) { // Get the labels for the types. var types = { feature: Language.$('default_update_possible_type_feature'), bugfix: Language.$('default_update_possible_type_bugfix'), other: Language.$('default_update_possible_type_other') }; // Create a var for the formated update history. var formatedUpdateHistory = ''; // Loop over all versions. for(var version in updateHistory) { // Create a headline for each version and start a table. formatedUpdateHistory += '

v ' + version + '


'; // Loop over all types. for(var type in updateHistory[version]) { // Create a table row for each type and start a list for the elements. formatedUpdateHistory += ''; } // End the table. formatedUpdateHistory += '
' + types[type] + '
    '; // Loop over the elements and add them to the list. for(var i = 0 ; i < updateHistory[version][type].length; i++) { formatedUpdateHistory += '
  • ' + updateHistory[version][type][i] + '
  • '; } // End the list. formatedUpdateHistory += '

'; } // Return the formated update history. return formatedUpdateHistory; } }; /** * Functions for language. */ Language = { /** * The name of the language which is actually set. */ name: 'English', /** * The text of the used language. */ text: null, /** * Init the language and set the used language code. */ init: function() { // Get the language code. var langCode = top.location.host.match(/^s[0-9]+-([a-zA-Z]+)\.ikariam\.gameforge\.com$/)[1]; // Set the language name. this.setLangName(!!langCode ? langCode : 'en'); // Set the text in the used language. this.setText(); }, /** * Set the name of the used language. * * @param String code * The laguage code. */ setLangName: function(code) { // All languages. var languageName = { ae: 'Arabic', ar: 'Spanish', ba: 'Bosnian', bg: 'Bulgarian', br: 'Portuguese', by: 'Russian', cl: 'Spanish', cn: 'Chinese', co: 'Spanish', cz: 'Czech', de: 'German', dk: 'Danish', ee: 'Estonian', en: 'English', es: 'Spanish', fi: 'Finish', fr: 'French', gr: 'Greek', hk: 'Chinese', hr: 'Bosnian', hu: 'Hungarian', id: 'Indonesian', il: 'Hebrew', it: 'Italian', kr: 'Korean', lt: 'Lithuanian', lv: 'Latvian', mx: 'Spanish', nl: 'Dutch', no: 'Norwegian', pe: 'Spanish', ph: 'Filipino', pk: 'Urdu', pl: 'Polish', pt: 'Portuguese', ro: 'Romanian', rs: 'Serbian', ru: 'Russian', se: 'Swedish', si: 'Slovene', sk: 'Slovak', tr: 'Turkish', tw: 'Chinese', ua: 'Ukranian', us: 'English', ve: 'Spanish', vn: 'Vietnamese', yu: 'Bosnian' }[code]; // Look up if implemented contains the language. for(var i = 0; i < languageInfo.implemented.length; i++) { // If the language is implemented set the name to it and return. if(languageInfo.implemented[i] == languageName) { this.name = languageName; return; } } // If the language is not implemented, set the language to english. this.name = 'English'; }, /* * Set the text for the script. */ setText: function() { // If a resource is used for the language text, use it. if(languageInfo.useResource) { // Get the ressource. var text = myGM.getResourceParsed('language' + this.name); // Store it to Language.text. this.text = (text && !text.is_error) ? text : languageInfo.defaultText; // Otherwise: Use the text in languageInfo. } else { // Get the text. var text = languageInfo['text'][this.name]; // Store it to Language.text. this.text = (text && !text.is_error) ? text : languageInfo.text.English; } }, /** * Return the name of the actually used language. * * @return String * The country code. */ getLangName: function() { return this.name; }, /** * Synonymous function for Language.getText(). * * @param String name * The name of the placeholder. * * @return mixed * The text. */ $: function(name) { return this.getText(name); }, /** * Return the name of the actually used language. * * @param String name * The name of the placeholder. * * @return mixed * The text. */ getText: function(name) { // Set the text to the placeholder. var erg = name; // Split the placeholder. var parts = name.split('_'); // If the splitting was successful. if(parts) { // Set txt to the "next level". var txt = this.text ? this.text[parts[0]] : null; // Loop over all parts. for(var i = 1; i < parts.length; i++) { // If the "next level" exists, set txt to it. if(txt && typeof txt[parts[i]] != 'undefined') { txt = txt[parts[i]]; } else { txt = erg; break; } } // If the text type is not an object, a function or undefined. if(typeof txt != 'object' && typeof txt != 'function' && typeof txt != 'undefined') { erg = txt; } } // Return the text. return erg; } }; /******************************************************** ********************************************************* ***** ***** ***** End of functions / variables for all Scripts. ***** ***** ***** ********************************************************* ********************************************************/ /********************** *** Ikariam Script. *** **********************/ /** * Storage for the unsafeWindow.ikariam funtion. */ var ika; /** * General functions. */ General = { /** * Init the script. */ init: function() { // Set the general used Script styles. this.setStyles(); // Set unsafeWindow.ikariam to ika for easier access. ika = win.ikariam; // Get the id of the body. var viewId = document.body.id; // Get the name of the view depending on the body id. switch(viewId) { case 'worldmap_iso': View.name = 'world'; break; case 'island': View.name = 'island'; break; case 'city': View.name = 'town'; break; default: break; } // Add the script toolbar. myGM.addElement('div', myGM.$('#GF_toolbar'), 'toolbar'); }, /** * Set the general script styles. */ setStyles: function() { // Add the general used styles. myGM.addStyle( "#" + myGM.prefix + "toolbar { position: absolute; top: 0px; right: 0px; } \ .bottomLine { border-bottom: 1px dotted #CCA569; } \ .minimizeImg, .maximizeImg { background: url('skin/interface/window_control_sprite.png') no-repeat scroll 0 0 transparent; cursor: pointer; display: block; height: 18px; width: 18px; } \ .minimizeImg { background-position: -144px 0; } \ .minimizeImg:hover { background-position: -144px -19px; } \ .maximizeImg { background-position: -126px 0; } \ .maximizeImg:hover { background-position: -126px -19px; }" ); // Set the notification style. myGM.addStyle( "." + myGM.prefix + "notificationBackground { z-index: 1000000000000; position: fixed; visibility: visible; top: 0px; left: 0px; width: 100%; height: 100%; padding: 0; background-color: #000; opacity: .7; } \ ." + myGM.prefix + "notificationPanelContainer { z-index: 1000000000001; position: fixed; visibility: visible; top: 100px; left: 50%; width: 500px; height: 370px; margin-left: -250px; padding: 0; text-align: left; color: #542C0F; font: 12px Arial,Helvetica,sans-serif; } \ ." + myGM.prefix + "notificationPanel { position: relative; top: 0px; left: 0px; background-color: transparent; border: 0 none; overflow: hidden; } \ ." + myGM.prefix + "notificationPanelHeader { height: 39px; background: none repeat scroll 0 0 transparent; font-weight: bold; line-height: 2; white-space: nowrap; } \ ." + myGM.prefix + "notificationPanelHeaderL { height: 39px; background-image: url('skin/layout/notes_top_left.png'); background-position: left top; background-repeat: no-repeat; } \ ." + myGM.prefix + "notificationPanelHeaderR { height: 39px; background-image: url('skin/layout/notes_top_right.png'); background-position: right top; background-repeat: no-repeat; } \ ." + myGM.prefix + "notificationPanelHeaderM { height: 39px; margin: 0 14px 0 38px; padding: 12px 0 0; background-image: url('skin/layout/notes_top.png'); background-position: left top; background-repeat: repeat-x; color: #811709; line-height: 1.34em; } \ ." + myGM.prefix + "notificationPanelBody { max-height: 311px; height: 100%; background: none repeat scroll 0 0 transparent; } \ ." + myGM.prefix + "notificationPanelBodyL { height: 100%; background-image: url('skin/layout/notes_left.png'); background-position: left top; background-repeat: repeat-y; } \ ." + myGM.prefix + "notificationPanelBodyR { height: 100%; background-image: url('skin/layout/notes_right.png'); background-position: right top; background-repeat: repeat-y; } \ ." + myGM.prefix + "notificationPanelBodyM { height: 100%; background-color: #F7E7C5; background-image: none; margin: 0 6px; padding: 0 10px; font-size: 14px; } \ ." + myGM.prefix + "notificationPanelBodyMTop { max-height: 100px; line-height: 2; } \ ." + myGM.prefix + "notificationPanelBodyMTop b { line-height: 3.5; font-size:110%; } \ ." + myGM.prefix + "notificationPanelBodyM a { color: #811709; font-weight: bold; } \ ." + myGM.prefix + "notificationPanelBodyM h2 { font-weight: bold; } \ ." + myGM.prefix + "notificationPanelBodyMContent { max-height: 270px; padding: 10px; background: url('skin/input/textfield.png') repeat-x scroll 0 0 #FFF7E1; border: 1px dotted #C0C0C0; font: 14px Arial,Helvetica,sans-serif; color: #000000; border-collapse: separate; overflow-y:auto; } \ ." + myGM.prefix + "notificationPanelBodyMBottom { max-height: 170px; padding: 10px; background: url('skin/input/textfield.png') repeat-x scroll 0 0 #FFF7E1; border: 1px dotted #C0C0C0; font: 14px Arial,Helvetica,sans-serif; color: #000000; border-collapse: separate; overflow-y:auto; } \ ." + myGM.prefix + "notificationPanelBodyPlaceholder { height: 20px; } \ ." + myGM.prefix + "notificationPanelFooter { height: 20px; background: none repeat scroll 0 0 transparent; } \ ." + myGM.prefix + "notificationPanelFooterL { height: 100%; background-image: url('skin/layout/notes_left.png'); background-position: left top; background-repeat: repeat-y; border: 0 none; } \ ." + myGM.prefix + "notificationPanelFooterR { height: 21px; background-image: url('skin/layout/notes_br.png'); background-position: right bottom; background-repeat: no-repeat; } \ ." + myGM.prefix + "notificationPanelFooterM { background-color: #F7E7C5; border-bottom: 3px solid #D2A860; border-left: 2px solid #D2A860; margin: 0 23px 0 3px; padding: 3px 0 2px 3px; font-size: 77%; } \ ." + myGM.prefix + "notificationPanelClose { cursor: pointer; position: absolute; top: 12px; right: 8px; width: 17px; height: 17px; background-image: url('skin/layout/notes_close.png'); } \ ." + myGM.prefix + "notificationPanelButtonWrapper { bottom: -4px; position: absolute; margin: 10px auto; width: 100%; text-align: center; } \ ." + myGM.prefix + "notificationPanelButton { background: url('skin/input/button.png') repeat-x scroll 0 0 #ECCF8E; border-color: #C9A584 #5D4C2F #5D4C2F #C9A584; border-style: double; border-width: 3px; cursor: pointer; display: inline; font-weight: bold; margin: 0px 5px; padding: 2px 10px; text-align: center; font-size: 12px; width: 100px; } \ ." + myGM.prefix + "notificationPanelButton:hover { color: #B3713F; } \ ." + myGM.prefix + "notificationPanelButton:active { border-color: #5D4C2F #C9A584 #C9A584 #5D4C2F; border-style: double; border-width: 3px; padding: 3px 10px 1px; } \ ." + myGM.prefix + "notificationPanelButtonConfirm { } \ ." + myGM.prefix + "notificationPanelButtonAbort { }", 'notification', true ); // Set the updater style. myGM.addStyle( "." + myGM.prefix + "updateTable { border-collapse: separate; border-spacing: 2px; } \ ." + myGM.prefix + "updateDataType { width: 100px; padding: 5px 0px 5px 5px; border: 1px solid #D2A860; } \ ." + myGM.prefix + "updateDataInfo { width: 300px; padding: 5px 5px 5px 20px; border: 1px solid #D2A860; } \ ." + myGM.prefix + "updateDataInfo ul li { list-style: disc outside none; }", 'updater', true ); }, /** * Parses a string number to an int value. * * @param String txt * The number to format. * * @return int * The formated value. */ getInt: function(txt) { // Return the formated number. return parseInt(txt.replace(/(\.|,)/g, '')); }, /** * Returns the value of the selected option of a select field. * * @param String id * The last part of the id of the element. * @param boolean hasNoPrefix * Says if the id has no prefix. * * @return String * The value. */ getSelectValue: function(id, hasNoPrefix) { // Get the select field. var select = myGM.$('#' + (hasNoPrefix ? '' : myGM.prefix) + id); // Return the value. return select.options[select.selectedIndex].value; }, /** * Returns a code consisting of the server name and the country code. * * @return string * The code. */ getServerCode: function() { // Split the host string. var lang = top.location.host.split('.'); // Set the language name. return (lang ? lang[1] + '_' + lang[0] : 'undefined'); }, /** * Formats a number to that format that is used in Ikariam. * * @param int num * The number to format. * @param boolean || boolean[] addColor * If the number should be coloured. (optional, if not set, a color will be used for negative and no color will be used for positive numbers) * * @return String * The formated number. */ formatToIkaNumber: function(num, addColor, usePlusSign) { var txt = num + ''; // Set a seperator every 3 digits from the end. txt = txt.replace(/(\d)(?=(\d{3})+\b)/g, '$1' + Language.$('settings_kiloSep')); // If the number is negative and it is enabled, write it in red. if(num < 0 && !(addColor == false || (addColor && addColor.negative == false))) { txt = '' + txt + ''; } // If the number is positive. if(num > 0) { // Add the plus sign if wanted. txt = (usePlusSign ? '+' : '') + txt; // Color the text green if wanted. if(!!(addColor == true || (addColor && addColor.positive == true))) { txt = '' + txt + ''; } } // Return the formated number. return txt; }, /** * Shows a hint to the user. * * @param String located * The location of the hint. Possible are all advisors, a clicked element or a committed element. * @param String type * The type of the hint. Possible is confirm, error, neutral or follow the mouse. * @param String msgText * The hint text. * @param String msgBindTo * An element the tooltip is binded (only used if located = committedElement). * @param String msgIsMinSize * If the message is minimized (only used if type = followMouse). */ showTooltip: function(located, type, msgText, msgBindTo, msgIsMinSize) { // Get the message location. var msgLocation = -1; switch(located) { case 'cityAdvisor': msgLocation = 1; break; case 'militaryAdvisor': msgLocation = 2; break; case 'researchAdvisor': msgLocation = 3; break; case 'diplomacyAdvisor': msgLocation = 4; break; case 'clickedElement': msgLocation = 5; break; case 'committedElement': msgLocation = 6; break; } // Get the message type. var msgType = -1; switch(type) { case 'confirm': msgType = 10; break; case 'error': msgType = 11; break; case 'neutral': msgType = 12; break; case 'followMouse': msgType = 13; break; } // Show the tooltip. ika.controller.tooltipController.bindBubbleTip(msgLocation, msgType, msgText, null, msgBindTo, msgIsMinSize); }, /** * Toogle the show / hide Button image and title. * * @param Element button * The button to toggle. */ toggleShowHideButton: function(button) { // Switch the button picture. button.classList.toggle('minimizeImg'); button.classList.toggle('maximizeImg'); // Switch the button title. if(button.title == Language.$('general_hide')) { button.title = Language.$('general_show'); } else { button.title = Language.$('general_hide'); } } }; /** * Functions for event handling. */ EventHandling = { /** * Events for the upkeep reduction tables. */ upkeepReductionTable: { /** * Toggles the visibility of the reduction information rows. */ toggle: function(e) { // Toggle the button. General.toggleShowHideButton(this); // Get the table rows. var tr = myGM.$$('tr', this.parentNode.parentNode.parentNode); // Toggle the visibility of all table rows except the first. for(var i = 1; i < tr.length; i++) { tr[i].classList.toggle('invisible'); } // Adjust the size of the Scrollbar. ika.controller.adjustSizes(); } }, /** * Events for the zoom function. */ zoomFunction: { /** * Zoom in when clicking on the zoom in button. */ zoomIn: function() { // Get the zoom factor. var factor = myGM.getValue('zoom_' + View.name + 'Factor', 100) + ZoomFunction.zoomStep; // Zoom. ZoomFunction.zoom(factor); }, /** * Zoom out when clicking on the zoom out button. */ zoomOut: function() { // Get the zoom factor. var factor = myGM.getValue('zoom_' + View.name + 'Factor', 100) - ZoomFunction.zoomStep; // Zoom. ZoomFunction.zoom(factor); }, /** * Zoom if the mouse is scrolled. */ mouseScroll: function(e) { // Check if the required keys are pressed. var keysPressed = myGM.getValue('zoom_ctrlPressed', true) ? !!e.ctrlKey : true && myGM.getValue('zoom_altPressed', false) ? !!e.altKey : true && myGM.getValue('zoom_shiftPressed', false) ? !!e.shiftKey : true; // If the required keys are pressed. if(keysPressed) { // If the scrolling is horizontally return. if (e.axis !== undefined && e.axis === e.HORIZONTAL_AXIS) { return; } // Strorage for the number of steps to scroll. var stepNumber = 0; // Get the number of steps to scroll. if (e.wheelDelta) { stepNumber = e.wheelDelta / 120; } if (e.detail) { stepNumber = -e.detail / 3; } if (e.wheelDeltaY !== undefined) { stepNumber = e.wheelDeltaY / 120; } // If the number is between -1 and 0, set it to -1. if(stepNumber < 0) { stepNumber = stepNumber > -1 ? -1 : Math.round(stepNumber); // If the number is between 0 and 1, set it to 1. } else { stepNumber = stepNumber < 1 ? 1 : Math.round(stepNumber); } // Get the zoom factor. var factor = myGM.getValue('zoom_' + View.name + 'Factor', 100) + ZoomFunction.zoomStep * stepNumber; // Zoom the view. ZoomFunction.zoom(factor); // Stop the default event. if(e.preventDefault) { e.preventDefault(); } else { return false; } } } }, /** * Events for the option panel. */ optionPanel: { /** * Toggles the visibility of the option wrapper contents. */ toggle: function(e) { // Toggle the button. General.toggleShowHideButton(this); // Toggle the visibility of the content. myGM.$('.content', this.parentNode.parentNode).classList.toggle('invisible'); // Store the visibility. var optionId = this.parentNode.parentNode.id.replace(myGM.prefix, ''); OptionPanel.optionVisibility[optionId] = !OptionPanel.optionVisibility[optionId]; myGM.setValue('optionPanel_optionVisibility', OptionPanel.optionVisibility); // Adjust the size of the Scrollbar. ika.controller.adjustSizes(); }, /** * Save the settings in the option panel. */ saveSettings: function() { OptionPanel.saveSettings(); } }, /** * Events for the info link. */ memberInfo: { /** * Is called after the info link is clicked. */ clickShow: function() { // Set the flag showing the link was klicked. myGM.setValue('memberInfo_infoLinkClicked', true); // Set the search settings so that the needed view is shown. myGM.$('#tab_highscore input[name="searchUser"]').value = ''; myGM.$('#searchOnlyFriends').checked = false; myGM.$('#searchOnlyAllies').checked = true; // Start the search. myGM.$('#tab_highscore input[type="submit"]').click(); }, /** * Is called after the reset button is clicked. */ clickReset: function() { // Store the member information and the actual time. myGM.setValue(General.getServerCode() + '_memberInfo_data_' + MemberInfo.type, MemberInfo.data); myGM.setValue(General.getServerCode() + '_memberInfo_time_' + MemberInfo.type, (new Date).getTime()); // Update the view. EventHandling.memberInfo.clickShow(); } }, /** * Events for missing resources. */ missingResources: { /** * If the resources are updated. */ resourcesUpdated: function(e) { // If the popup is closed, remove the action listener. if(!myGM.$('#buildingUpgrade')) { myGM.$('#cityResources').removeEventListener('DOMSubtreeModified', EventHandling.missingResources.resourcesUpdated, false); // Otherwise: update the resources if necessary. } else { // Timeout to have access to GM_ funtions. setTimeout(function() { MissingResources.show(true, true); }, 0); } } }, /** * Functions for replaced urls. */ replacedUrl: { /** * Is called after a replaced url is clicked. */ click: function(e) { // The link which should be opened. var linkToOpen = this.innerHTML.decodeHTML(); // Set the notification text. var notificationText = { header: Language.$('replacedUrl_notification_header'), body: Language.$('replacedUrl_notification_text1') + '"' + linkToOpen + '"' + Language.$('replacedUrl_notification_text2'), confirm: Language.$('general_yes'), abort: Language.$('general_no') }; // Set the notification callback. var notificationCallback = { confirm: function() { win.open(linkToOpen); } }; // Show the notification. myGM.notification(notificationText, notificationCallback); } }, /** * Functions for unit info. */ unitInfo: { /** * Is called after the "show info" button is clicked. */ click: function() { UnitInfo.showPopup(); } }, /** * Events for loading preview. */ loadingPreview: { /** * Is called after an attribute of loadingPreview was modified. * * @param event e * The calling event. */ attrModified: function(e) { // If the attribute was changed. if(e.attrChange == MutationEvent.MODIFICATION) { // If the style.display is set to none. if(e.attrName.trim() == 'style' && e.newValue.search(/display: none/i) != -1) { // Timeout to have access to GM_ funtions. setTimeout(EnhancedView.getPopup, 0); } } } } }; /** * Functions for enhanced view. */ EnhancedView = { /** * Inits the enhanced view. * Adds the event listener to the loadingPreview. */ init: function() { // Wait for a popup. myGM.$('#loadingPreview').addEventListener('DOMAttrModified', EventHandling.loadingPreview.attrModified, false); // Init parts which are not shown in popups. this.initStatic(); }, /** * Inits the modifications on the website which are not shown in popups. */ initStatic: function() { // Hide the Bird animation. if(myGM.getValue('module_hideBirdsActive', true)) View.hideBirds(); // Init the Zoom function. if(myGM.getValue('module_zoomActive', true)) ZoomFunction.init(); // Init the circular message link. if(myGM.getValue('module_easyCircularMsgActive', true)) Message.easyCircularMessage(); // Init the function for showing the resource information. if(myGM.getValue('module_resourceInfoActive', true) || myGM.getValue('module_capacityInfoActive', true)) ResourceInfo.init(); // Move loading circle. if(myGM.getValue('module_lcMoveActive', true)) View.moveLoadingCircle(); // Init the function for showing the missing resources. if(myGM.getValue('module_missingResActive', true)) MissingResources.init(); // Init the function for easy access of some popups. if(myGM.getValue('module_easyAccessActive', true)) ResourceInfo.addRessourceLinks(); // Don't center town advisor. if(myGM.getValue('module_nctAdvisorActive', true)) View.noCenterTownAdvisor(); // If the military tooltip should be shown without mouseover or click, add the styles. if(myGM.getValue('module_directMilitaryTtActive', true)) Tooltips.initDirectMilitaryTooltip(); // Set the colonizing links. if(myGM.getValue('module_colonizingLinksActive', true) && View.name == 'island') City.setColonizingLinks(); // Set the styles for the if(myGM.getValue('module_replaceUrlsActive', true)) Message.setStyleForReplaceUrl(); // Init the function for showing the missing resources. if(myGM.getValue('module_memberInfoActive', false)) MemberInfo.init(); }, /** * Calls the script module depending on the popup. */ getPopup: function() { // Update resource information. if(myGM.getValue('module_resourceInfoActive', true)) ResourceInfo.updateHourlyResourceInfo(); // Set the colonizing links. if(myGM.getValue('module_colonizingLinksActive', true) && View.name == 'island') City.setColonizingLinks(); // If the script was already executed on this popup. if(myGM.$('#' + myGM.prefix + 'alreadyExecutedPopup')) return; // Get the popup. var popup = myGM.$('.templateView'); // Get the popup id. var popupId = popup ? popup.id.replace('_c', '') : ''; // If a popup exists, add the hint, that the popup script was executed. if(popup) { var alreadyExecuted = myGM.addElement('input', myGM.$('.mainContent', popup), 'alreadyExecutedPopup'); alreadyExecuted.type = 'hidden'; } /****************************************************** *** Functions that should only run once on a popup. *** ******************************************************/ // Select the modules of the script which should be executed. switch(popupId) { // Options popup. case 'options': OptionPanel.show(); break; // Finance popup. case 'finances': if(myGM.getValue('module_incomeActive', true)) Balance.incomeOnTop(); if(myGM.getValue('module_urtShortActive', true)) Balance.shortUpkeepReductionTable(); break; // Military view popup. case 'militaryAdvisor': if(myGM.getValue('module_directMilitaryTtActive', true)) Tooltips.directMilitaryTooltip(); if(myGM.getValue('module_ttAutoActive', true)) Tooltips.autoshowInMilitaryView(); break; // Diplomacy ally view popup. case 'diplomacyAlly': if(myGM.getValue('module_ttAutoActive', true)) Tooltips.autoshowInAllianceView(); break; // Show messages. case 'diplomacyAdvisor': case 'diplomacyAdvisorOutBox': case 'diplomacyAdvisorArchive': case 'diplomacyAdvisorArchiveOutBox': if(myGM.getValue('module_replaceUrlsActive', true)) Message.replaceUrl(); break; // Diplomacy ally view popup in embassy. case 'embassy': if(myGM.getValue('module_ttAutoActive', true)) Tooltips.autoshowInAllianceView(); break; // Building ground popup. case 'buildingGround': if(myGM.getValue('module_missingResActive', true)) MissingResources.showInBuildingGround(); break; // Write message popup. case 'sendIKMessage': if(myGM.getValue('module_messageSigActive', true)) Message.addSignature(); break; // Troops in town popup. case 'cityMilitary': if(myGM.getValue('module_unitInfoActive', true)) UnitInfo.addPopupLink(); break; // Highscore popup. case 'highscore': if(myGM.getValue('module_memberInfoActive', false)) MemberInfo.show(); break; } // Building view. if(myGM.$('#buildingUpgrade') && myGM.getValue('module_missingResActive', true)) MissingResources.showInSidebar(); } }; /** * Functions for the general view. */ View = { /** * Storage for the name of the view. */ name: '', /** * Move loading circle to breadcrumb. */ moveLoadingCircle: function() { // Add the styles. myGM.addStyle( "#js_worldBread { margin-left: 16px !important; } \ #loadingPreview { transform: scale(0.5); -o-transform: scale(0.5); -webkit-transform: scale(0.5); left: 35px !important; top: 141px !important; }" ); }, /** * Hide the bird animation but no other animation. */ hideBirds: function() { // Add the style. myGM.addStyle( ".bird_swarm { visibility: hidden !important; }" ); }, /** * Don't center the city and date in town advisor vertically. */ noCenterTownAdvisor: function() { // Add the style. myGM.addStyle( "#inboxCity td { vertical-align: top !important; }" ); } }; /** * Functions for tooltips. */ Tooltips = { /** * Show tooltips in alliance view automatically. */ autoshowInAllianceView: function() { // Enable toggling on mouseover / mouseout. this.autoshowGeneral('cityInfo'); }, /** * Show tooltips in military advisor view automatically. */ autoshowInMilitaryView: function() { // Enable toggling on mouseover / mouseout. this.autoshowGeneral('spyMilitary', myGM.getValue('module_directMilitaryTtActive', true)); }, /** * Show tooltips with class name magnifierClass automatically. * * @param String magnifierClass * The class of the tooltips to enable toggling. * @param boolean deleteOnClick * If the onClick event should be deleted or just moved to mouseover. */ autoshowGeneral: function(magnifierClass, deleteOnClick) { // Get all magnifiers. var magnifier = myGM.$$('.' + magnifierClass); // Set the mousover and mouseout for all magnifiers. for(var i = 0; i < magnifier.length; i++) { // Get the onclick event and "delete" the old one. var magOnClick = magnifier[i].onclick; magnifier[i].onclick = 'return false;'; // If the on click event should be moved. if(!deleteOnClick) { // Add the show event. var magIcon = myGM.$('.magnify_icon', magnifier[i]); if(!magIcon) magIcon = magnifier[i]; magIcon.addEventListener('mouseover', function(e) { ika.controller.captureMousePosition(e); this(e); }.bind(magOnClick), true); } } // If the on click event should be moved. if(!deleteOnClick) { // Trigger the close event with jQuery on a click on the popup. myGM.$('.templateView .mainContent').addEventListener('click', function() { win.$(document).trigger("closeExclusiveInfo"); }, true); } }, /** * Shows the military tooltip without mouseover. */ initDirectMilitaryTooltip: function() { // Add the styles. myGM.addStyle( "#js_MilitaryMovementsFleetMovementsTable .military_event_table .magnify_icon { background-image: none; cursor: default; width: 240px; } \ #js_MilitaryMovementsFleetMovementsTable .military_event_table .magnify_icon .infoTip { display: inline; position: relative; padding: 0px; border: none; } \ #js_MilitaryMovementsFleetMovementsTable .military_event_table .magnify_icon .infoTip h5 { display: none; } \ #js_MilitaryMovementsFleetMovementsTable .military_event_table .icon40 { background-size: 25px 25px; background-color: transparent; padding: 26px 3px 0px 3px; width: 30px; } \ #js_MilitaryMovementsFleetMovementsTable .military_event_table .icon40.resource_icon { background-size: 20px 16px; }" ); }, /** * Hide the ship number of own transports and add titles to the loaded troops / ships / resources. */ directMilitaryTooltip: function() { // Get the table rows. var militaryEventTableTr = myGM.$$('#js_MilitaryMovementsFleetMovementsTable .military_event_table tr'); // Loop at the table rows. for(var i = 1; i < militaryEventTableTr.length; i++) { // Get the mission div. var missionDiv = myGM.$('td:nth-child(1) div.mission_icon', militaryEventTableTr[i]); // If it is your own table row and the mission is transport or trade. if(militaryEventTableTr[i].classList.contains('own') && (missionDiv.classList.contains('transport') || missionDiv.classList.contains('trade'))) { // Hide the table cell. myGM.$('td:nth-child(4) div', militaryEventTableTr[i]).classList.add('invisible'); } } // Storage for the unit / ships / resources classes and names. var idTranslation = new Array( { classId: 'swordsman', langId: 'name_unit_swordsman' }, { classId: 'phalanx', langId: 'name_unit_phalanx' }, { classId: 'archer', langId: 'name_unit_archer' }, { classId: 'marksman', langId: 'name_unit_marksman' }, { classId: 'mortar', langId: 'name_unit_mortar' }, { classId: 'slinger', langId: 'name_unit_slinger' }, { classId: 'catapult', langId: 'name_unit_catapult' }, { classId: 'ram', langId: 'name_unit_ram' }, { classId: 'steamgiant', langId: 'name_unit_steamgiant' }, { classId: 'bombardier', langId: 'name_unit_bombardier' }, { classId: 'cook', langId: 'name_unit_cook' }, { classId: 'medic', langId: 'name_unit_medic' }, { classId: 'gyrocopter', langId: 'name_unit_gyrocopter' }, { classId: 'spearman', langId: 'name_unit_spearman' }, { classId: 'ship_balliasta', langId: 'name_ship_balliasta' }, { classId: 'ship_catapult', langId: 'name_ship_catapult' }, { classId: 'ship_flamethrower', langId: 'name_ship_flamethrower' }, { classId: 'ship_mortar', langId: 'name_ship_mortar' }, { classId: 'ship_ram', langId: 'name_ship_ram' }, { classId: 'ship_steamboat', langId: 'name_ship_steamboat' }, { classId: 'ship_rocketship', langId: 'name_ship_rocketship' }, { classId: 'ship_submarine', langId: 'name_ship_submarine' }, { classId: 'ship_paddlespeedship', langId: 'name_ship_paddlespeedship' }, { classId: 'ship_balloncarrier', langId: 'name_ship_balloncarrier' }, { classId: 'ship_tender', langId: 'name_ship_tender' }, { classId: 'ship_transport', langId: 'name_ship_transport' }, { classId: 'ship_premium_transport', langId: 'name_ship_transport' }, { classId: 'gold', langId: 'name_resource_gold' }, { classId: 'wood', langId: 'name_resource_wood' }, { classId: 'wine', langId: 'name_resource_wine' }, { classId: 'marble', langId: 'name_resource_marble' }, { classId: 'glass', langId: 'name_resource_glass' }, { classId: 'sulfur', langId: 'name_resource_sulfur' } ); // Get the event table. var movementsTable = myGM.$('#js_MilitaryMovementsFleetMovementsTable'); // Add the unit names. for(var i = 0; i < idTranslation.length; i++) { var detailIcon = myGM.$$('.icon40.' + idTranslation[i].classId, movementsTable); for(var k = 0; k < detailIcon.length; k++) { detailIcon[k].title = Language.$(idTranslation[i].langId); } } } }; /** * Functions for balance view. */ Balance = { /** * Shows the actual income also on top of the site. */ incomeOnTop: function() { // Get the table for the summary. var summaryTable = myGM.$('.table01'); // Show the income on top. this.showIncomeOnTop(summaryTable); // Adjust the size of the Scrollbar. ika.controller.adjustSizes(); }, /** * Show the actual income on top of the site. * * @param element summaryTable * The table for the summary. */ showIncomeOnTop: function(summaryTable) { // Get the actual income. var income = this.getIncome(); // Create the rows for the income per day and the income per day. var incomeRow = myGM.addElement('tr', summaryTable, null, new Array('result', 'alt')); var incomeRow24h = myGM.addElement('tr', summaryTable, null, 'result'); // Create the content of the table rows. this.createTableRow(new Array(Language.$('balance_income_perHour'), '', '', General.formatToIkaNumber(income)), new Array('sigma', ['value', 'res'], ['value', 'res'], ['value', 'res']), incomeRow, false); this.createTableRow(new Array(Language.$('balance_income_perDay'), '', '', General.formatToIkaNumber(income * 24)), new Array('sigma', ['value', 'res'], ['value', 'res'], ['value', 'res']), incomeRow24h, false); }, /** * Gets the actual income from the Ikariam page and returns it. * * @return int * The actual income */ getIncome: function() { // Get the table cell with the actual income. var incomeCell = myGM.$$('.hidden')[myGM.$$('.hidden').length - 1]; // If the content of the cell is not just the income move one element inwards. while(incomeCell.firstChild.firstChild) { incomeCell = incomeCell.firstChild; } // Get the actual income. var txt = incomeCell.innerHTML; // Remove the thousand seperators. return General.getInt(txt); }, /** * Shows a short upkeep reduction table. */ shortUpkeepReductionTable: function() { // Get the upkeep redutcion tables. var uRT = myGM.$$('.upkeepReductionTable'); if(uRT.length == 0) { uRT = myGM.$$('#upkeepReductionTable'); } // Create an array for data storage. var row = { reason: new Array(), basicUpkeep: new Array(), supplyUpkeep: new Array(), result: new Array() }; // Get the data for the troops and ships redution rows. for(var i = 0; i < 3; i++) { row.reason.push(Language.$('balance_upkeep_reason_' + i)); row.basicUpkeep.push(General.getInt(myGM.$$('.altbottomLine td.hidden, .result td.hidden, .alt.bottomLine td.hidden, .result td.hidden', uRT[0])[i].innerHTML)); row.supplyUpkeep.push(General.getInt(myGM.$$('.altbottomLine td.hidden, .result td.hidden, .alt.bottomLine td.hidden, .result td.hidden', uRT[1])[i].innerHTML)); row.result.push(row.basicUpkeep[i] + row.supplyUpkeep[i]); } // Get the start income. var beforeReduction = General.getInt(myGM.$('td.hidden', uRT[2]).innerHTML); // Get the result income. var income = this.getIncome(); // Create the table to show the var shortTable = myGM.addElement('table', uRT[0].parentNode, null, new Array('table01', 'border', 'left'), null, null, uRT[0]); shortTable.id = 'balance'; // Create the table head. this.createTableRow(new Array('', Language.$('balance_upkeep_basic'), Language.$('balance_upkeep_supply'), Language.$('balance_upkeep_result')), new Array('city', ['value', 'res'], ['value', 'res'], ['value', 'res']), myGM.addElement('tr', shortTable), true); // Create the start income row. var startRow = myGM.addElement('tr', shortTable, null, new Array('alt', 'bottomLine')); this.createTableRow(new Array(Language.$('balance_income_start'), '', '', General.formatToIkaNumber(beforeReduction)), new Array('city', ['value', 'res'], ['value', 'res'], ['value', 'res']), startRow, false); // Create the troops / ships redution rows. for(var i = 0; i < 3; i++) { var newRow = myGM.addElement('tr', shortTable, null, (i % 2 == 1) ? new Array('alt', 'bottomLine') : ''); this.createTableRow(new Array(row.reason[i], General.formatToIkaNumber(-row.basicUpkeep[i]), General.formatToIkaNumber(-row.supplyUpkeep[i]), General.formatToIkaNumber(-row.result[i])), new Array('city', ['value', 'res'], ['value', 'res'], 'hidden'), newRow, false); } // Create the result row. var resultRow = myGM.addElement('tr', shortTable, null, 'result'); this.createTableRow(new Array('Summe', '', '', General.formatToIkaNumber(income)), new Array('sigma', ['value', 'res'], ['value', 'res'], 'hidden'), resultRow, false); // Create the spacing between the tables. myGM.addElement('hr', uRT[0].parentNode, null, null, null, null, uRT[0]); // Hide the data rows of the tables and add the show button. for(var i = 0; i < uRT.length; i++) { // Get all rows. var tr = myGM.$$('tr', uRT[i]); // Hide all rows except the first. for(var k = 1; k < tr.length; k++) { tr[k].classList.add('invisible'); } // Add the show button to the first row. var th = myGM.$('th', tr[0]); var btn = myGM.addElement('div', th, null, 'maximizeImg', new Array(['cssFloat', 'left']), th.firstChild); btn.title = Language.$('general_show'); // Add the event listener. btn.addEventListener('click', EventHandling.upkeepReductionTable.toggle, false); } // Adjust the size of the Scrollbar. ika.controller.adjustSizes(); }, /** * Adds cells to a table row. * * @param String[] cellText * Array with the text of the cells. * @param String[] cellClassName * Array with the classes of the cells. * @param element row * Table row where the cells should be added. * @param boolean head * If the row is a table head row. */ createTableRow: function(cellText, cellClassName, row, head) { // Do this for every cell. for(var i = 0; i < cellText.length; i++) { // Add the cell. var cell = myGM.addElement(head ? 'th' : 'td', row, null, cellClassName[i]); // Set the content of the cell. cell.innerHTML = cellText[i]; } } }; /** * Functions for option panel. */ OptionPanel = { /** * Storage for option visibility. */ optionVisibility: { moduleOption: true }, /** * Adds the tab for the script options. */ show: function() { // Get the script options tab. var tabGMOptions = myGM.$('#tabScriptOptions'); // If the script options tab doesn't exists, create it. if(!tabGMOptions) { // Set the styles. this.setStyles(); // Add the GM tab link to the tab menu. var tabmenu = myGM.$('.tabmenu'); var jsTabGMOptions = myGM.addElement('li', tabmenu, 'js_tabScriptOptions', 'tab', null, false); jsTabGMOptions.innerHTML = ' ' + Language.$('optionPanel_scripts') + ' '; jsTabGMOptions.setAttribute('onclick', "switchTab('tabScriptOptions');"); // Add the content wrapper for the GM tab to the tab menu. var mainContent = myGM.$('#tabGameOptions').parentNode; tabGMOptions = myGM.addElement('div', mainContent, 'tabScriptOptions', null, new Array(['display', 'none']), false); } // Fill the tab with content. this.createTabContent(tabGMOptions); }, /** * Sets the styles that are used for the update-panel. */ setStyles: function() { // Add all styles to the ikariam page. myGM.addStyle( "#js_tabGameOptions, #js_tabAccountOptions, #js_tabFacebookOptions, #js_tabOpenIDOptions, #js_tabScriptOptions { width: 130px !important; margin-left: 5px !important; border-radius: 5px 5px 0px 0px } \ ." + myGM.prefix + "SignatureInput { resize: none; width: 99%; height: 75px; } \ #tabScriptOptions hr { margin: 0; } \ .cbWrapper { margin: 0 0 0 10px; }" ); }, /** * Creates the content of the tab. * * @param element tab * The tab where the content should be added. */ createTabContent: function(tab) { // Get the option visibility. this.optionVisibility = myGM.getValue('optionPanel_optionVisibility', this.optionVisibility); // Create the wrapper for the enabling / disabling of modules. var moduleContentWrapper = this.createOptionsWrapper(tab, Language.$('optionPanel_section_module_title'), 'moduleOption'); this.createModuleContent(moduleContentWrapper); // Create the wrapper for the update settings. var updateContentWrapper = this.createOptionsWrapper(tab, Language.$('optionPanel_section_update_title'), 'updateOption'); this.createUpdateContent(updateContentWrapper); // Create the wrapper for the resource information / missing resources. var rimrContentWrapper = this.createOptionsWrapper(tab, Language.$('optionPanel_section_resInfoMissingRes_title'), 'resInfoMissingResOption'); this.createResInfoMissingResContent(rimrContentWrapper); // Create the wrapper for the zoom settings. var zoomContentWrapper = this.createOptionsWrapper(tab, Language.$('optionPanel_section_zoom_title'), 'zoomOption'); this.createZoomContent(zoomContentWrapper); // Create the wrapper for the zoom settings. var sigContentWrapper = this.createOptionsWrapper(tab, Language.$('optionPanel_section_messageSignature_title'), 'messageSignatureOption'); this.createMessageSigContent(sigContentWrapper); }, /** * Create a wrapper for a section on the option panel. * * @param element tab * The tab where the wrapper should be added. * @param String headerText * The text of the header. * @param String id * The id of the option wrapper. * * @return element * The wrapper for the content of the options. */ createOptionsWrapper: function(tab, headerText, id) { // Get the content show status. var showContent = !!this.optionVisibility[id]; // Create the wrapper. var optionsWrapper = myGM.addElement('div', tab, id, 'contentBox01h'); // Create the header. var optionsHeader = myGM.addElement('h3', optionsWrapper, null, 'header'); optionsHeader.innerHTML = headerText; // Add the show / hide button. var btn = myGM.addElement('div', optionsHeader, null, showContent ? 'minimizeImg' : 'maximizeImg', new Array(['cssFloat', 'left'])); btn.addEventListener('click', EventHandling.optionPanel.toggle, false); btn.title = showContent ? Language.$('general_hide') : Language.$('general_show'); // Create the content wrapper. var optionsWrapperContent = myGM.addElement('div', optionsWrapper, null, showContent ? 'content' : new Array('content', 'invisible')); // Create the footer. myGM.addElement('div', optionsWrapper, null, 'footer'); // Return the content wrapper. return optionsWrapperContent; }, /** * Creates the content of the module part. * * @param element contentWrapper * The wrapper where the content should be added. */ createModuleContent: function(contentWrapper) { // Create options table. var updateTable = this.addOptionsTable(contentWrapper); // Set the checkbox data. var cbData = new Array( { id: 'update', checked: myGM.getValue('module_updateActive', true), label: Language.$('optionPanel_section_module_label_updateActive'), hrAfter: true }, { id: 'incomeOnTop', checked: myGM.getValue('module_incomeActive', true), label: Language.$('optionPanel_section_module_label_incomeOnTopActive'), hrAfter: false }, { id: 'upkeepReduction', checked: myGM.getValue('module_urtShortActive', true), label: Language.$('optionPanel_section_module_label_upkeepReductionActive'), hrAfter: false }, { id: 'missingResources', checked: myGM.getValue('module_missingResActive', true), label: Language.$('optionPanel_section_module_label_missingResActive'), hrAfter: false }, { id: 'resourceInformation', checked: myGM.getValue('module_resourceInfoActive', true), label: Language.$('optionPanel_section_module_label_resourceInfoActive'), hrAfter: false }, { id: 'capacityInformation', checked: myGM.getValue('module_capacityInfoActive', true), label: Language.$('optionPanel_section_module_label_capacityInfoActive'), hrAfter: false }, { id: 'easyAccess', checked: myGM.getValue('module_easyAccessActive', true), label: Language.$('optionPanel_section_module_label_easyAccessActive'), hrAfter: false }, { id: 'zoom', checked: myGM.getValue('module_zoomActive', true), label: Language.$('optionPanel_section_module_label_zoomActive'), hrAfter: true }, { id: 'messageSignature', checked: myGM.getValue('module_messageSigActive', true), label: Language.$('optionPanel_section_module_label_messageSignatureActive'), hrAfter: false }, { id: 'easyCircularMessage', checked: myGM.getValue('module_easyCircularMsgActive', true), label: Language.$('optionPanel_section_module_label_easyCircularMsgActive'), hrAfter: false }, { id: 'replaceUrls', checked: myGM.getValue('module_replaceUrlsActive', true), label: Language.$('optionPanel_section_module_label_replaceUrlsActive'), hrAfter: false }, { id: 'colonizingLinks', checked: myGM.getValue('module_colonizingLinksActive', true), label: Language.$('optionPanel_section_module_label_colonizingLinksActive'), hrAfter: true }, { id: 'loadingCircleMove', checked: myGM.getValue('module_lcMoveActive', true), label: Language.$('optionPanel_section_module_label_lcMoveActive'), hrAfter: false }, { id: 'tooltipsAuto', checked: myGM.getValue('module_ttAutoActive', true), label: Language.$('optionPanel_section_module_label_tooltipsAutoActive'), hrAfter: false }, { id: 'directMilitaryTooltip', checked: myGM.getValue('module_directMilitaryTtActive', true), label: Language.$('optionPanel_section_module_label_directMilitaryTtActive'), hrAfter: false }, { id: 'unitInfo', checked: myGM.getValue('module_unitInfoActive', true), label: Language.$('optionPanel_section_module_label_unitInfoActive'), hrAfter: true }, { id: 'hideBirds', checked: myGM.getValue('module_hideBirdsActive', true), label: Language.$('optionPanel_section_module_label_hideBirdsActive'), hrAfter: false }, { id: 'noCenterTownAdvisor', checked: myGM.getValue('module_nctAdvisorActive', true), label: Language.$('optionPanel_section_module_label_nctAdvisorActive'), hrAfter: false }, { id: 'memberInformation', checked: myGM.getValue('module_memberInfoActive', false), label: Language.$('optionPanel_section_module_label_memberInfoActive'), hrAfter: false } ); // Create the checkboxes. this.addCheckboxes(updateTable, cbData); // Add the button to save the settings. this.addSaveButton(contentWrapper); }, /** * Creates the content of the update part. * * @param element contentWrapper * The wrapper where the content should be added. */ createUpdateContent: function(contentWrapper) { // Create options table. var updateTable = this.addOptionsTable(contentWrapper); // Array for update interval values and names. var opts = new Array( { value: 3600, name: Language.$('optionPanel_section_update_label_interval_option_hour') }, { value: 43200, name: Language.$('optionPanel_section_update_label_interval_option_hour12') }, { value: 86400, name: Language.$('optionPanel_section_update_label_interval_option_day') }, { value: 259200, name: Language.$('optionPanel_section_update_label_interval_option_day3') }, { value: 604800, name: Language.$('optionPanel_section_update_label_interval_option_week') }, { value: 1209600, name: Language.$('optionPanel_section_update_label_interval_option_week2') }, { value: 2419200, name: Language.$('optionPanel_section_update_label_interval_option_week4') } ); // Create the update interval select. this.addSelect(updateTable, 'updateInterval', myGM.getValue('updater_updateInterval', 3600), opts, Language.$('optionPanel_section_update_label_interval_description')); // Prepare the update link table row. var updateLinkTr = this.addOptionsTableRow(updateTable, true); updateLinkTr.firstChild.classList.add('center'); updateLinkTr.firstChild.classList.remove('left'); // Add the link for manual updates. var updateLink = myGM.addElement('a', updateLinkTr.firstChild); updateLink.href = '#'; updateLink.innerHTML = Language.$('optionPanel_section_update_label_manual_text1') + '"' + scriptInfo.name + '"' + Language.$('optionPanel_section_update_label_manual_text2'); updateLink.addEventListener('click', Updater.doManualUpdate, false); // Add the button to save the settings. this.addSaveButton(contentWrapper); }, /** * Create the content of the resource information / missing resources part. * * @param element contentWrapper * The wrapper where the content should be added. */ createResInfoMissingResContent: function(contentWrapper) { // Create options table. var selectionTable = this.addOptionsTable(contentWrapper); var checkboxTable = this.addOptionsTable(contentWrapper); // Array for update interval values and names. var optsAlign = new Array( { value: 'alignRight', name: Language.$('optionPanel_section_resInfoMissingRes_label_align_right') }, { value: 'alignLeft', name: Language.$('optionPanel_section_resInfoMissingRes_label_align_left') }, { value: 'withSeparation', name: Language.$('optionPanel_section_resInfoMissingRes_label_align_rightWithSeparation') } ); // Create the hourly income style select. this.addSelect(selectionTable, 'hourlyIncomeStyle', myGM.getValue('resourceInfo_hourlyIncomeStyle', 'alignRight'), optsAlign, Language.$('optionPanel_section_resInfoMissingRes_label_hourlyIncomeStyle')); // Array for update interval values and names. var optsOrientation = new Array( { value: 'vertical', name: Language.$('optionPanel_section_resInfoMissingRes_label_orientation_vertical') }, { value: 'horizontal', name: Language.$('optionPanel_section_resInfoMissingRes_label_orientation_horizontal') }, { value: 'horizontalFull', name: Language.$('optionPanel_section_resInfoMissingRes_label_orientation_horizontalFull') } ); // Create the hourly income style select. this.addSelect(selectionTable, 'capacityInfoStyle', myGM.getValue('resourceInfo_capacityStyle_orientation', 'vertical'), optsOrientation, Language.$('optionPanel_section_resInfoMissingRes_label_capacityOrientationStyle')); // Get the checkbox data. var cbData = new Array( { id: 'hasBorder', checked: myGM.getValue('resourceInfo_capacityStyle_hasBorder', true), label: Language.$('optionPanel_section_resInfoMissingRes_label_hasBorder'), hrAfter: false }, { id: 'showBranchRes', checked: myGM.getValue('resourceInfo_showBranchRes', true), label: Language.$('optionPanel_section_resInfoMissingRes_label_showBranchRes'), hrAfter: true }, { id: 'showPositive', checked: myGM.getValue('missingRes_showPositive', true), label: Language.$('optionPanel_section_resInfoMissingRes_label_showPositive'), hrAfter: false }, { id: 'showColoured', checked: myGM.getValue('missingRes_showColoured', true), label: Language.$('optionPanel_section_resInfoMissingRes_label_showColoured'), hrAfter: false } ); // Create the checkboxes. this.addCheckboxes(checkboxTable, cbData); // Add the button to save the settings. this.addSaveButton(contentWrapper); }, /** * Create the content of the zoom part. * * @param element contentWrapper * The wrapper where the content should be added. */ createZoomContent: function(contentWrapper) { // Create the options tables. var factorTable = this.addOptionsTable(contentWrapper); var scaleChildrenTable = this.addOptionsTable(contentWrapper); var accessKeysTable = this.addOptionsTable(contentWrapper); // Set the description data. var descriptionRowData = new Array( { parent: scaleChildrenTable, text: Language.$('optionPanel_section_zoom_label_scaleChildren_description') }, { parent: accessKeysTable, text: Language.$('optionPanel_section_zoom_label_accessKeys') } ); // Add the descriptions. for(var i = 0; i < descriptionRowData.length; i++) { // Add the table row. var descriptionTr = this.addOptionsTableRow(descriptionRowData[i]['parent'], true); // Add the description. var description = myGM.addElement('p', descriptionTr.firstChild); description.innerHTML = descriptionRowData[i]['text']; } // Set the data for the factor select fields. var factorSelects = new Array( { id: 'zoomWorld', selected: myGM.getValue('zoom_worldFactor', 100), labelText: Language.$('optionPanel_section_zoom_label_zoomFactor_world') }, { id: 'zoomIsland', selected: myGM.getValue('zoom_islandFactor', 100), labelText: Language.$('optionPanel_section_zoom_label_zoomFactor_island') }, { id: 'zoomTown', selected: myGM.getValue('zoom_townFactor', 100), labelText: Language.$('optionPanel_section_zoom_label_zoomFactor_town') } ); // Set the zoom factor values and names. var opts = new Array(); for(var i = ZoomFunction.minZoom; i <= ZoomFunction.maxZoom; i = i + ZoomFunction.zoomStep) { opts.push({ value: i, name: i + '%' }); } // Add all factor select fields. for(var i = 0; i < factorSelects.length; i++) { this.addSelect(factorTable, factorSelects[i]['id'], factorSelects[i]['selected'], opts, factorSelects[i]['labelText']); } // Set the scale children checkbox data. var scaleChildrenCbData = new Array( { id: 'zoomScaleChildrenWorld', checked: myGM.getValue('zoom_worldScaleChildren', true), label: Language.$('optionPanel_section_zoom_label_scaleChildren_world') }, { id: 'zoomScaleChildrenIsland', checked: myGM.getValue('zoom_islandScaleChildren', true), label: Language.$('optionPanel_section_zoom_label_scaleChildren_island') }, { id: 'zoomScaleChildrenTown', checked: myGM.getValue('zoom_townScaleChildren', true), label: Language.$('optionPanel_section_zoom_label_scaleChildren_town') } ); // Add the scale children checkboxes this.addCheckboxes(scaleChildrenTable, scaleChildrenCbData); // Set the scale children checkbox data. var accessKeysCbData = new Array( { id: 'zoomCtrlPressed', checked: myGM.getValue('zoom_ctrlPressed', true), label: Language.$('general_ctrl') }, { id: 'zoomAltPressed', checked: myGM.getValue('zoom_altPressed', false), label: Language.$('general_alt') }, { id: 'zoomShiftPressed', checked: myGM.getValue('zoom_shiftPressed', false), label: Language.$('general_shift') } ); // Add the scale children checkboxes this.addCheckboxes(accessKeysTable, accessKeysCbData); // Add the button to save the settings. this.addSaveButton(contentWrapper); }, /** * Create the content of the message signature part. * * @param element contentWrapper * The wrapper where the content should be added. */ createMessageSigContent: function(contentWrapper) { // Create the options tables. var settingsTable = this.addOptionsTable(contentWrapper); var globalSigTable = this.addOptionsTable(contentWrapper); var serverSigTable = this.addOptionsTable(contentWrapper); // Get the checkbox data. var cbData = new Array( { id: 'useServerSignature', checked: myGM.getValue('messageSignature_useServerSignature_' + General.getServerCode(), false), label: Language.$('optionPanel_section_messageSignature_label_useServerSignature') }, { id: 'placementTop', checked: myGM.getValue('messageSignature_placementTop', true), label: Language.$('optionPanel_section_messageSignature_label_placementTop') } ); // Create the checkboxes. this.addCheckboxes(settingsTable, cbData); // Set the text area data. var textAreaData = new Array( { id: 'globalSignatureText', parent: globalSigTable, text: Language.$('optionPanel_section_messageSignature_label_signatureText_global'), value: myGM.getValue('messageSignature_globalSignature', '') }, { id: 'serverSignatureText', parent: serverSigTable, text: Language.$('optionPanel_section_messageSignature_label_signatureText_server'), value: myGM.getValue('messageSignature_serverSignature_' + General.getServerCode(), '') } ); // Add the text areas. for(var i = 0; i < textAreaData.length; i++) { // Add the label table row. var labelTr = this.addOptionsTableRow(textAreaData[i]['parent'], true); // Add the label. var label = myGM.addElement('p', labelTr.firstChild); label.innerHTML = textAreaData[i]['text']; // Add the text field table row. var textAreaTr = this.addOptionsTableRow(textAreaData[i]['parent'], true); // Add the text area. var textArea = myGM.addElement('textarea', textAreaTr.firstChild, textAreaData[i]['id'], new Array('textfield', myGM.prefix + 'SignatureInput')); textArea.value = textAreaData[i]['value']; } // Add the button to save the settings. this.addSaveButton(contentWrapper); }, /** * Add a new options table. * * @param element wrapper * The wrapper where the table should be added. * * @return element * The body of the new table. */ addOptionsTable: function(wrapper) { // Create table and tablebody. var table = myGM.addElement('table', wrapper, null, new Array('moduleContent', 'table01')); var tableBody = myGM.addElement('tbody', table); // Return the table body. return tableBody; }, /** * Adds a new table row to a options table. * * @param element optionsTableBody * The table body where the table should be added. * @param boolean oneCell * Decides if there is one cell or there are two cells. * * @return element * The new table row. */ addOptionsTableRow: function(optionsTableBody, oneCell) { // Create the new table row. var tr = myGM.addElement('tr', optionsTableBody); // Create first cell. var td1 = myGM.addElement('td', tr); // If just ond cell. if(oneCell) { // Set width of cell to width of two cells. td1.colSpan = 2; td1.classList.add('left'); // Otherwise. } else { // Create second cell. myGM.addElement('td', tr, null, 'left'); } // Return the table row. return tr; }, /** * Creates new checkboxes and adds it to a option table, specified as parentTable. * * @param element parentTable * The parent of the new checkboxes. * @param mixed[] cbData * A array containing the data of the checkboxes. */ addCheckboxes: function(parentTable, cbData) { // Create the checkboxes. for(var i = 0; i < cbData.length; i++) { // Create table row. var tr = this.addOptionsTableRow(parentTable, true); // Create the wrapper for the checkbox and the label. var wrapper = myGM.addElement('div', tr.firstChild, null, 'cbWrapper'); // Create the checkbox and set the attributes. var cb = myGM.addElement('input', wrapper, cbData[i]['id'] + 'Cb', 'checkbox'); cb.type = 'checkbox'; cb.title = cbData[i]['label']; cb.checked = cbData[i]['checked'] ? 'checked' : ''; if(cbData[i]['hrAfter'] == true) { var spacer = this.addOptionsTableRow(parentTable, true); myGM.addElement('hr', spacer.firstChild); } } // Replace the checkboxes for better appearance. ika.controller.replaceCheckboxes(); }, /** * Creates a new select field and adds it to a option table, specified as parentTable. * * @param element parentTable * The parent table of the new select field. * @param String id * The last part of the id of the select field. * @param boolean selected * The value of the selected option. * @param mixed[] opts * An array with the names an values of the options. * @param String labelText * The text of the select label. */ addSelect: function(parentTable, id, selected, opts, labelText) { // Create table row. var tr = this.addOptionsTableRow(parentTable, false); // Create label. var selectLabel = myGM.addElement('span', tr.firstChild); selectLabel.innerHTML = labelText; // Create the wrapper for the select. var wrapper = myGM.addElement('div', tr.lastChild, id + 'SelectContainer', new Array('select_container', 'size175'), new Array(['position', 'relative'])); // Create the select field. var select = myGM.addElement('select', wrapper, id + 'Select', 'dropdown'); // Add the Options. for(var i = 0; i < opts.length; i++) { // Create an option. var option = myGM.addElement('option', select); // Set the value and the name. option.value = opts[i]['value']; option.innerHTML = opts[i]['name']; // If the option is selected, set selected to true. if(option.value == selected) { option.selected = 'selected'; } } // Replace the dropdown for better appearance. ika.controller.replaceDropdownMenus(); }, /** * Creates a commit Button and adds it to a parent. * * @param element parent * The parent element. * * @return element * The save button. */ addSaveButton: function(parent) { // Create the button wrapper. var buttonWrapper = myGM.addElement('div', parent, null, 'centerButton'); // Create the button. var saveButton = myGM.addElement('input', buttonWrapper, null, 'button'); saveButton.type = 'submit'; saveButton.value = Language.$('optionPanel_save'); // Add a click action to the button. saveButton.addEventListener('click', EventHandling.optionPanel.saveSettings, false); return saveButton; }, /** * Save the settings. */ saveSettings: function() { // Save the module settings. myGM.setValue('module_updateActive', myGM.$('#' + myGM.prefix + 'updateCb').checked); myGM.setValue('module_incomeActive', myGM.$('#' + myGM.prefix + 'incomeOnTopCb').checked); myGM.setValue('module_urtShortActive', myGM.$('#' + myGM.prefix + 'upkeepReductionCb').checked); myGM.setValue('module_missingResActive', myGM.$('#' + myGM.prefix + 'missingResourcesCb').checked); myGM.setValue('module_resourceInfoActive', myGM.$('#' + myGM.prefix + 'resourceInformationCb').checked); myGM.setValue('module_capacityInfoActive', myGM.$('#' + myGM.prefix + 'capacityInformationCb').checked); myGM.setValue('module_easyAccessActive', myGM.$('#' + myGM.prefix + 'easyAccessCb').checked); myGM.setValue('module_zoomActive', myGM.$('#' + myGM.prefix + 'zoomCb').checked); myGM.setValue('module_messageSigActive', myGM.$('#' + myGM.prefix + 'messageSignatureCb').checked); myGM.setValue('module_easyCircularMsgActive', myGM.$('#' + myGM.prefix + 'easyCircularMessageCb').checked); myGM.setValue('module_replaceUrlsActive', myGM.$('#' + myGM.prefix + 'replaceUrlsCb').checked); myGM.setValue('module_colonizingLinksActive', myGM.$('#' + myGM.prefix + 'colonizingLinksCb').checked); myGM.setValue('module_lcMoveActive', myGM.$('#' + myGM.prefix + 'loadingCircleMoveCb').checked); myGM.setValue('module_ttAutoActive', myGM.$('#' + myGM.prefix + 'tooltipsAutoCb').checked); myGM.setValue('module_directMilitaryTtActive', myGM.$('#' + myGM.prefix + 'directMilitaryTooltipCb').checked); myGM.setValue('module_unitInfoActive', myGM.$('#' + myGM.prefix + 'unitInfoCb').checked); myGM.setValue('module_hideBirdsActive', myGM.$('#' + myGM.prefix + 'hideBirdsCb').checked); myGM.setValue('module_nctAdvisorActive', myGM.$('#' + myGM.prefix + 'noCenterTownAdvisorCb').checked); myGM.setValue('module_memberInfoActive', myGM.$('#' + myGM.prefix + 'memberInformationCb').checked); // Save the update settings. myGM.setValue('updater_updateInterval', General.getInt(General.getSelectValue('updateIntervalSelect'))); // Save the resource information / missing resources settings. myGM.setValue('resourceInfo_hourlyIncomeStyle', General.getSelectValue('hourlyIncomeStyleSelect')); myGM.setValue('resourceInfo_capacityStyle_orientation', General.getSelectValue('capacityInfoStyleSelect')); myGM.setValue('resourceInfo_capacityStyle_hasBorder', myGM.$('#' + myGM.prefix + 'hasBorderCb').checked); myGM.setValue('resourceInfo_showBranchRes', myGM.$('#' + myGM.prefix + 'showBranchResCb').checked); myGM.setValue('missingRes_showPositive', myGM.$('#' + myGM.prefix + 'showPositiveCb').checked); myGM.setValue('missingRes_showColoured', myGM.$('#' + myGM.prefix + 'showColouredCb').checked); // Save the zoom function settings. myGM.setValue('zoom_worldFactor', General.getInt(General.getSelectValue('zoomWorldSelect'))); myGM.setValue('zoom_islandFactor', General.getInt(General.getSelectValue('zoomIslandSelect'))); myGM.setValue('zoom_townFactor', General.getInt(General.getSelectValue('zoomTownSelect'))); myGM.setValue('zoom_worldScaleChildren', myGM.$('#' + myGM.prefix + 'zoomScaleChildrenWorldCb').checked); myGM.setValue('zoom_islandScaleChildren', myGM.$('#' + myGM.prefix + 'zoomScaleChildrenIslandCb').checked); myGM.setValue('zoom_townScaleChildren', myGM.$('#' + myGM.prefix + 'zoomScaleChildrenTownCb').checked); myGM.setValue('zoom_ctrlPressed', myGM.$('#' + myGM.prefix + 'zoomCtrlPressedCb').checked); myGM.setValue('zoom_altPressed', myGM.$('#' + myGM.prefix + 'zoomAltPressedCb').checked); myGM.setValue('zoom_shiftPressed', myGM.$('#' + myGM.prefix + 'zoomShiftPressedCb').checked); // Save the message signature function settings. myGM.setValue('messageSignature_useServerSignature_' + General.getServerCode(), myGM.$('#' + myGM.prefix + 'useServerSignatureCb').checked); myGM.setValue('messageSignature_placementTop', myGM.$('#' + myGM.prefix + 'placementTopCb').checked); myGM.setValue('messageSignature_globalSignature', myGM.$('#' + myGM.prefix + 'globalSignatureText').value); myGM.setValue('messageSignature_serverSignature_' + General.getServerCode(), myGM.$('#' + myGM.prefix + 'serverSignatureText').value); // Show success hint. General.showTooltip('cityAdvisor', 'confirm', Language.$('general_successful')); } }; /** * Functions for zooming world, island and town view. */ ZoomFunction = { /** * The minimal zoom factor in percent. */ minZoom: 55, /** * The maximal zoom factor in percent. */ maxZoom: 150, /** * The step between two zoom factors in percent. */ zoomStep: 5, /** * Init the zooming. */ init: function() { // Get the min zoom. var minZoom = Math.round(ika.worldview_scale_min * 100); minZoom = minZoom + ((minZoom % 5 == 0) ? 0 : (5 - minZoom % 5)); // Set the max and min zoom. this.minZoom = minZoom; ika.worldview_scale_min = minZoom / 100; ika.worldview_scale_max = this.maxZoom / 100; // Change the mousewheel listener, so that it is possible to cheack if there are also keys pressed. this.changeMouseWheelListener(); // Get the zooming factor. var factor = myGM.getValue('zoom_' + View.name + 'Factor', 100); // Add the zoom Buttons. this.addZoomButtons(); // Zoom. this.zoom(factor); }, /** * Add the Buttons for zooming to the view. */ addZoomButtons: function() { // Get the the script toolbar var scriptToolbar = myGM.$('#' + myGM.prefix + 'toolbar'); // Create the zoom buttons. var zoomWrapper = myGM.addElement('div', scriptToolbar, 'zoomWrapper'); var zoomIn = myGM.addElement('div', zoomWrapper, 'zoomIn', 'maximizeImg'); var zoomFactor = myGM.addElement('div', zoomWrapper, 'zoomFactor'); var zoomOut = myGM.addElement('div', zoomWrapper, 'zoomOut', 'minimizeImg'); // Set the button title. zoomIn.title = Language.$('zoomFunction_zoomIn'); zoomFactor.title = Language.$('zoomFunction_zoomFactor'); zoomOut.title = Language.$('zoomFunction_zoomOut'); // Add the event listener. zoomIn.addEventListener('click', EventHandling.zoomFunction.zoomIn, false); zoomOut.addEventListener('click', EventHandling.zoomFunction.zoomOut, false); // Add the styles. myGM.addStyle( "#" + myGM.prefix + "zoomWrapper { position: absolute; top: 2px; right: 0px; width: 72px; transform: scale(0.75); scale(0.75); -o-transform: scale(0.75); -webkit-transform: scale(0.75); } \ #" + myGM.prefix + "zoomIn { position: absolute; } \ #" + myGM.prefix + "zoomFactor { position: absolute; left: 20px; width: 35px; text-align: center; } \ #" + myGM.prefix + "zoomOut { position: absolute; left: 58px; }" ); }, /** * Changes the mouse wheel listener so that it could be used with access keys. */ changeMouseWheelListener: function() { // Set the defaut scrollDiv id. var scrollDivId = '#worldmap'; // If wolrd view, change the scroll div id. if(View.name == 'world') { scrollDivId = '#map1'; } // Remove the old mouse wheel listener. (with the use of Ikariam-jQuery) win.$(scrollDivId).unbind('mousewheel'); // Add the new mouse wheel listener. var scrollDiv = myGM.$(scrollDivId); scrollDiv.addEventListener('DOMMouseScroll', EventHandling.zoomFunction.mouseScroll, false); scrollDiv.addEventListener('mousewheel', EventHandling.zoomFunction.mouseScroll, false); }, /** * Zooms the view. * * @param int factor * The factor which is used. */ zoom: function(factor) { // If the factor is bigger / smaller than allowed, set it to the max / min allowed. factor = factor > this.maxZoom ? this.maxZoom : (factor < this.minZoom ? this.minZoom : factor); // Store the zoom factor. myGM.setValue('zoom_' + View.name + 'Factor', factor); // Update the zoom factor which is shown to the user and the zoom buttons. var zoomFactorDiv = myGM.$('#' + myGM.prefix + 'zoomFactor'); var zoomIn = myGM.$('#' + myGM.prefix + 'zoomIn'); var zoomOut = myGM.$('#' + myGM.prefix + 'zoomOut'); // Zoom factor. if(zoomFactorDiv) { zoomFactorDiv.innerHTML = factor + '%'; } // Zoom in. if(zoomIn) { // If it is not allowed to zoom in, hide the zoom in button. if(factor >= this.maxZoom) { zoomIn.classList.add('invisible'); // Otherwise: Show the zoom in button. } else { zoomIn.classList.remove('invisible'); } } // Zoom out. if(zoomOut) { // If it is not allowed to zoom out, hide the zoom out button. if(factor <= this.minZoom) { zoomOut.classList.add('invisible'); // Otherwise: Show the zoom out button. } else { zoomOut.classList.remove('invisible'); } } // Get the factor as normal number, not as percentage. var factorNew = factor / 100.0; // Get the game scaling factor depending on the view. var scale = 0; switch(View.name) { case 'world': break; case 'island': scale = ika.worldview_scale_island; break; case 'town': scale = ika.worldview_scale_city; break; default: return; break; } // It is the world view, zoom it. if(View.name == 'world') { this.zoomWorld(factorNew); // Otherwise call the ikariam zoom function. } else { // Get the number of steps to zoom. var stepNumber = Math.round((factorNew - scale) / .05); // Get the center position of the worldmap. var worldview = myGM.$('#worldview'); var posX = worldview.offsetLeft + worldview.offsetWidth / 2; var posY = worldview.offsetTop + worldview.offsetHeight / 2; // Zoom. ika.controller.scaleWorldMap(stepNumber, posX, posY); } // Scale child elements if enabled. if(myGM.getValue('zoom_' + View.name + 'ScaleChildren', true)) ZoomFunction.scaleChildren(factorNew); // Return the factor in percent. return factor; }, /** * Zoom the world view. * * @param float factor * The factor which is used. */ zoomWorld: function(factor) { // Get the factor the scrollcover must be moved. var translateXY = (100 - 100 / factor) / 2; // Get the new height and width of the scrollcover. var heightWidth = 100 / factor; // Overwrite the old style. myGM.addStyle( "#scrollcover { transform: scale(" + factor + ") translate(" + translateXY + "%, " + translateXY + "%); -o-transform: scale(" + factor + ") translate(" + translateXY + "%, " + translateXY + "%); -webkit-transform: scale(" + factor + ") translate(" + translateXY + "%, " + translateXY + "%); height: " + heightWidth + "% !important; width: " + heightWidth + "% !important; }", 'zoomWorld', true ); // Get the map. var map = myGM.$('#map1'); // Set the new offset. var newWmTop = 0; var newWmLeft = 0; // Apply the new offset to the map. map.style.top = newWmTop + 'px'; map.style.left = newWmLeft + 'px'; }, /** * Scales the children of the worldmap / island view. * * @param float factor * The factor which is used. * @param String view * The name of the view. */ scaleChildren: function(factor) { // Which view is used? switch(View.name) { // Worldview. case 'world': myGM.addStyle( ".islandTile .wonder, .islandTile .tradegood, .islandTile .cities" + (factor < 1 ? ", .ownerState" : "") + " { transform: scale(" + 1 / factor + "); -o-transform: scale(" + 1 / factor + "); -webkit-transform: scale(" + 1 / factor + "); } \ .islandTile .cities { bottom: 10px !important; }", 'scaleChildren', true ); break; // Island view. case 'island': myGM.addStyle( ".cityLocation .scroll_img { transform: scale(" + 1 / factor + "); -o-transform: scale(" + 1 / factor + "); -webkit-transform: scale(" + 1 / factor + "); }", 'scaleChildren', true ); break; // Town view. case 'town': myGM.addStyle( ".building .timetofinish { transform: scale(" + 1 / factor + "); -o-transform: scale(" + 1 / factor + "); -webkit-transform: scale(" + 1 / factor + "); }", 'scaleChildren', true ); break; // Default: do nothing. default: return; break; } } }; /** * Function for showing the missing resources directly in the ruction / update view. */ MissingResources = { /** * Init the missing ressources. */ init: function() { // Set the required styles. myGM.addStyle( "#sidebar #buildingUpgrade ul.resources li { width: 120px; } \ #sidebar #buildingUpgrade ul.resources li.time { width: 60px !important; } \ #sidebar ." + myGM.prefix + "missingResources { float: right; }" ); }, /** * Shows the missing resources in the building ground popup. */ showInBuildingGround: function() { // Show the resources. this.show(false); }, /** * Shows the missing resources in the sidebar. */ showInSidebar: function() { // Show the resources. this.show(true); // Add an event listener to update the shown resources. myGM.$('#cityResources').addEventListener('DOMSubtreeModified', EventHandling.missingResources.resourcesUpdated, false); }, /** * Shows the missing resources in the place which is given. * * @param boolean isSidebar * If the place where to show is the sidebar. */ show: function(isSidebar, update) { // Store the current resources. var current = Array(); current.push(ika.model.currentResources.resource); // Wood. current.push(ika.model.currentResources[1]); // Wine. current.push(ika.model.currentResources[2]); // Marble. current.push(ika.model.currentResources[3]); // Crystal. current.push(ika.model.currentResources[4]); // Sulfur. // Store the resource names. var resourceName = new Array('wood', 'wine', 'marble', 'glass', 'sulfur'); // Get all possible wrappers. var wrapper = myGM.$$('#' + (isSidebar ? 'sidebar' : 'buildingGround') + ' .resources'); // Loop over all wrappers. for(var i = 0; i < wrapper.length; i++) { // Loop over all resources. for(var k = 0; k < resourceName.length; k++) { // Get the resource node. var node = myGM.$('.' + resourceName[k], wrapper[i]); // If the node exist, show the missing resources. if(node) { // Get the missing resource number. var missing = current[k] - (update ? General.getInt(node.lastChild.previousSibling.nodeValue) : General.getInt(node.lastChild.nodeValue)); // If there are resources missing. if(missing < 0 || (myGM.getValue('missingRes_showPositive', true) && isSidebar)) { // Show the missing resource info. var show = update ? myGM.$('#' + myGM.prefix + 'missingResources' + resourceName[k]) : myGM.addElement('span', node, 'missingResources' + resourceName[k], 'missingResources', null, true); show.innerHTML = (isSidebar ? '' : ' (') + General.formatToIkaNumber(missing, myGM.getValue('missingRes_showColoured', true), true) + (isSidebar ? '' : ')'); } } } } } }; /** * Functions for showing the hourly production directly in the town view. * Also adds the daily Production to the resource tooltip. */ ResourceInfo = { /** * Storage for the last tradegood. */ lastTradegood: null, /** * Inits the resource info. */ init: function() { // If the city is owned by the player. if(ika.model.isOwnCity == true) { // Set the styles. this.setStyles(); // Add the resource info. if(myGM.getValue('module_resourceInfoActive', true)) this.addHourlyResourceInfo(); // Add the capacity info. if(myGM.getValue('module_capacityInfoActive', true)) this.addCapacityInfo(); } }, /** * Sets the styles that are required for the resource info. */ setStyles: function() { // Probleme: resourceInfo deaktiviert + alle. var resourceInfoActive = myGM.getValue('module_resourceInfoActive', true); var capacityInfoActive = myGM.getValue('module_capacityInfoActive', true); var hourlyIncomeStyle = myGM.getValue('resourceInfo_hourlyIncomeStyle', 'alignRight'); var capacityStyleOrientation = myGM.getValue('resourceInfo_capacityStyle_orientation', 'vertical'); // If the resource info is active. if(resourceInfoActive) { // Define the style for the resources wrapper. var resourcesStyle = { lineHeight: (capacityInfoActive && capacityStyleOrientation == 'horizontal') ? 'line-height: 9px !important;' : 'line-height: 11px !important;', align: (hourlyIncomeStyle != 'alignLeft') ? ' text-align: right;' : '', fontSize: (capacityInfoActive && capacityStyleOrientation != 'horizontalFull') ? ' font-size: 11px;' : '', height: (capacityInfoActive && capacityStyleOrientation == 'horizontalFull') ? ' height: 32px !important;' : '', top: (capacityInfoActive && capacityStyleOrientation == 'horizontalFull') ? ' top: -2px !important;' : '', paddingLeft: (capacityInfoActive && capacityStyleOrientation == 'vertical' && hourlyIncomeStyle == 'alignLeft') ? ' padding-left: 38px !important;' : '' }; var resStyle = '#resources_wood, #resources_wine, #resources_marble, #resources_glass, #resources_sulfur { ' + resourcesStyle.lineHeight + resourcesStyle.align + resourcesStyle.fontSize + resourcesStyle.height + resourcesStyle.top + resourcesStyle.paddingLeft + ' } '; // Define the style for the global menu resources. var globalMenuStyle = { padding: (hourlyIncomeStyle != 'alignLeft') ? 'padding-right: 4px;' : '' }; var globMenuStyle = '#js_GlobalMenu_wood, #js_GlobalMenu_wine, #js_GlobalMenu_marble, #js_GlobalMenu_crystal, #js_GlobalMenu_sulfur { ' + globalMenuStyle.padding + ' } '; // Define the style for the hourly income. var incomeStyle = { fontSize: (capacityInfoActive && capacityStyleOrientation != 'horizontalFull') ? 'font-size: 9px;' : 'font-size: 11px;', paddingRight: (hourlyIncomeStyle != 'alignLeft') ? ' padding-right: 4px;' : '' }; var hirStyle = '.' + myGM.prefix + 'hourlyIncomeResource { ' + incomeStyle.fontSize + incomeStyle.paddingRight + ' } '; // Define the style for the resources wrapper with border as separation. var separationStyle = { border: (hourlyIncomeStyle == 'withSeparation') ? 'border-right: 1px dotted #542C0F;' : '' }; var sepStyle = '#resources_wood, #resources_wine, #resources_marble, #resources_glass { ' + separationStyle.border + ' } '; // Add all styles to the ikariam page. var resInfoStyle = resStyle + globMenuStyle + hirStyle + sepStyle; myGM.addStyle(resInfoStyle, 'hourlyIncomeStyle', true); // Otherwise: If the capacity info is active. } else if(capacityInfoActive) { // Define the style for the resources wrapper. var resourcesStyle = { lineHeight: (capacityStyleOrientation == 'horizontal') ? 'line-height: 12px !important;' : ((capacityStyleOrientation == 'vertical') ? ' line-height: 24px !important;' : ''), align: (hourlyIncomeStyle != 'alignLeft') ? ' text-align: right;' : '', fontSize: (capacityStyleOrientation == 'vertical') ? ' font-size: 11px;' : '', height: (capacityStyleOrientation == 'horizontalFull') ? ' height: 32px !important;' : '', top: (capacityStyleOrientation == 'horizontalFull') ? ' top: -2px !important;' : '', paddingLeft: (capacityStyleOrientation == 'vertical' && hourlyIncomeStyle == 'alignLeft') ? ' padding-left: 38px !important;' : '' }; var resStyle = '#resources_wood, #resources_wine, #resources_marble, #resources_glass, #resources_sulfur { ' + resourcesStyle.lineHeight + resourcesStyle.align + resourcesStyle.fontSize + resourcesStyle.height + resourcesStyle.top + resourcesStyle.paddingLeft + ' } '; // Define the style for the global menu resources. var globalMenuStyle = { padding: (hourlyIncomeStyle != 'alignLeft') ? 'padding-right: 4px;' : '' }; var globMenuStyle = '#js_GlobalMenu_wood, #js_GlobalMenu_wine, #js_GlobalMenu_marble, #js_GlobalMenu_crystal, #js_GlobalMenu_sulfur { ' + globalMenuStyle.padding + ' } '; // Define the style for the resources wrapper with border as separation. var separationStyle = { border: (hourlyIncomeStyle == 'withSeparation') ? 'border-right: 1px dotted #542C0F;' : '' }; var sepStyle = '#resources_wood, #resources_wine, #resources_marble, #resources_glass { ' + separationStyle.border + ' } '; // Add all styles to the ikariam page. var resInfoStyle = resStyle + globMenuStyle + sepStyle; myGM.addStyle(resInfoStyle, 'hourlyIncomeStyle', true); } // If the capacity info is active. if(capacityInfoActive) { // Define the style for the bar. var barStyle = '.' + myGM.prefix + 'bar { height: 100%; width: 100%; bottom: 0px; position: absolute; } '; // Define the style for the red bar. var redBarStyle = '.' + myGM.prefix + 'bar.' + myGM.prefix + 'red { background-color: #AA0000; } '; // Define the style for the red bar. var yellowBarStyle = '.' + myGM.prefix + 'bar.' + myGM.prefix + 'yellow { background-color: #FFD700; } '; // Define the style for the green bar. var greenBarStyle = '.' + myGM.prefix + 'bar.' + myGM.prefix + 'green { background-color: #669900; } '; // Define the style for the capacity information. var capacityInfoStyle = { position: 'position: absolute;', height: (capacityStyleOrientation == 'vertical') ? ' height: 21px;' : ' height: 4px;', width: (capacityStyleOrientation != 'vertical') ? ((capacityStyleOrientation == 'horizontal') ? ' width: 50px;' : ' width: 79px;') : ' width: 4px;', bottom: ' bottom: 4px;', right: (capacityStyleOrientation != 'vertical') ? ' right: 4px;' : '', marginLeft: (capacityStyleOrientation == 'vertical' && hourlyIncomeStyle == 'alignLeft') ? ' margin-left: -7px;' : '' }; var capInfoStyle = '.' + myGM.prefix + 'capacityInfo { ' + capacityInfoStyle.position + capacityInfoStyle.height + capacityInfoStyle.width + capacityInfoStyle.bottom + capacityInfoStyle.right + capacityInfoStyle.marginLeft + ' } '; // Define the style for the capacity information with border. var capacityInfoBorderStyle = { border: 'border: 1px inset #906646;', height: (capacityStyleOrientation == 'vertical') ? ' height: 20px;' : ' height: 3px;', width: (capacityStyleOrientation != 'vertical') ? ((capacityStyleOrientation == 'horizontal') ? ' width: 50px;' : ' width: 78px;') : ' width: 3px;', bottom: ' bottom: 3px;', right: (capacityStyleOrientation != 'vertical') ? ' right: 3px;' : '' }; var capInfoBorderStyle = '.' + myGM.prefix + 'capacityInfo.' + myGM.prefix + 'border { ' + capacityInfoBorderStyle.border + capacityInfoBorderStyle.height + capacityInfoBorderStyle.width + capacityInfoBorderStyle.bottom + capacityInfoBorderStyle.right + ' } '; // Add all styles to the ikariam page. var capacityInformationStyle = barStyle + redBarStyle + yellowBarStyle + greenBarStyle + capInfoStyle + capInfoBorderStyle; myGM.addStyle(capacityInformationStyle, 'capacityInfoStyle', true); } }, /** * Show the hourly resource info. */ addHourlyResourceInfo: function() { // If the info containers are already set, do nothing. if(myGM.$('#' + myGM.prefix + 'wood')) { return; } // Set all resource types. var types = new Array('wood', 'wine', 'marble', 'glass', 'sulfur'); // Loop over all resource types. for(var i = 0; i < types.length; i++) { // Add the hourly info. myGM.addElement('span', myGM.$('#resources_' + types[i]), 'hourlyIncomeResource' + types[i], 'hourlyIncomeResource', null, true); // Add the daily info. var parent = myGM.$('#resources_' + types[i] + ' .tooltip'); var classes = (i < 2) ? 'smallFont' : new Array('smallFont', 'invisible'); var wrapper = myGM.addElement('p', parent, 'dailyIncomeResourceWrapper' + types[i], classes, null, null, myGM.$('p:nth-child(2)', parent)); wrapper.innerHTML = Language.$('resourceInfo_dailyIncome_' + types[i]) + ' '; myGM.addElement('span', wrapper, 'dailyIncomeResource' + types[i]); } // Update the resource information. this.updateHourlyResourceInfo(true); }, /** * Update the shown hourly resource information. */ updateHourlyResourceInfo: function(firstRun) { // If the city is not owned by the player, stop the update. if(ika.model.isOwnCity != true) { return; } // Prefix for selection. var hourlyPrefix = '#' + myGM.prefix + 'hourlyIncomeResource'; var dailyPrefix = '#' + myGM.prefix + 'dailyIncomeResource'; // If the info containers are not set, set them. if(!myGM.$(hourlyPrefix + 'wood')) { this.init(); } // Set all resource types. var types = new Array('wood', 'wine', 'marble', 'glass', 'sulfur'); // Get some needed data. var producedTradegood = ika.model.producedTradegood; var tradegood = ika.model.tradegoodProduction * 3600 + 0.001; var resource = ika.model.resourceProduction * 3600 + 0.001; var wineSpending = ika.model.wineSpendings; var producesWine = ika.model.cityProducesWine; // Bugfix for the first running of the script on the page: wineSpending is not reduced by vineyard. if(firstRun) { // Get the vineyard. var vineyard = myGM.$('#locations .vineyard'); // If a vineyard exists, reduce the wine spending. if(vineyard) { // Get the vineyard level. var level = vineyard.className.match(/level([0-9]*)/i)[1]; // Reduce the wine spending. wineSpending = (wineSpending * (100 - level)) / 100; } } // Update the wood data. myGM.$(hourlyPrefix + types[0]).innerHTML = '
' + General.formatToIkaNumber(Math.floor(resource), true, true); myGM.$(dailyPrefix + types[0]).innerHTML = General.formatToIkaNumber(Math.floor(resource * 24), false); // Delete the last tradegood info. if(this.lastTradegood) { // Hourly. myGM.$(hourlyPrefix + types[this.lastTradegood]).innerHTML = ''; // Daily. myGM.$(dailyPrefix + types[this.lastTradegood]).innerHTML = ''; // Hide the daily tradegood info if it is not wine. if(this.lastTradegood != 1) myGM.$(dailyPrefix + 'Wrapper' + types[this.lastTradegood]).classList.add('invisible'); } // Add the info if the city produces wine. if(producesWine) { // Hourly. myGM.$(hourlyPrefix + types[producedTradegood]).innerHTML = '
' + General.formatToIkaNumber(Math.floor(tradegood - wineSpending), true, true); // Daily. myGM.$(dailyPrefix + types[producedTradegood]).innerHTML = General.formatToIkaNumber(Math.floor((tradegood - wineSpending) * 24), false); // Add the info if the city doesn't produces wine. } else { // Hourly. myGM.$(hourlyPrefix + types[producedTradegood]).innerHTML = '
' + General.formatToIkaNumber(Math.floor(tradegood), true, true); myGM.$(hourlyPrefix + types[1]).innerHTML = '
' + General.formatToIkaNumber(Math.floor(-1 * wineSpending), true, true); // Daily. myGM.$(dailyPrefix + types[producedTradegood]).innerHTML = General.formatToIkaNumber(Math.floor(tradegood * 24), false); myGM.$(dailyPrefix + types[1]).innerHTML = General.formatToIkaNumber(Math.floor(-1 * wineSpending * 24), false); } // Show the daily tradegood info. myGM.$(dailyPrefix + 'Wrapper' + types[producedTradegood]).classList.remove('invisible'); // Store the actual tradegood as last produced tradegood. this.lastTradegood = producedTradegood; }, /** * Add a bar showing the status of the filling of the warehouse. */ addCapacityInfo: function() { // If the info containers are already set, do nothing. if(myGM.$('#' + myGM.prefix + 'capacityInfowood')) { return; } // Set all resource types. var types = new Array('wood', 'wine', 'marble', 'glass', 'sulfur'); // Has the capacity information a border? var hasBorder = myGM.getValue('resourceInfo_capacityStyle_hasBorder', true); // Loop over all resource types. for(var i = 0; i < types.length; i++) { // Add the hourly info. var wrapper = myGM.addElement('div', myGM.$('#resources_' + types[i]), 'capacityInfo' + types[i], hasBorder ? new Array('capacityInfo', 'border') : 'capacityInfo', null, true); myGM.addElement('div', wrapper, 'maxCapacity' + types[i], new Array('bar', 'yellow'), null, true); myGM.addElement('div', wrapper, 'warehouseCapacity' + types[i], new Array('bar', 'green'), null, true); myGM.addElement('div', wrapper, 'currentResource' + types[i], new Array('bar', 'red'), null, true); } // Update the resource information. this.updateCapacityInfo(); // Add an event listener to update the shown resources. myGM.$('#cityResources').addEventListener('DOMSubtreeModified', ResourceInfo.updateCapacityInfo, false); }, /** * Update the bar showing the status of the filling of the warehouse. */ updateCapacityInfo: function() { // If the city is not owned by the player, stop the update. if(ika.model.isOwnCity != true) { return; } // Prefix for selection and user deifned settings. var prefix = '#' + myGM.prefix; var orientation = myGM.getValue('resourceInfo_capacityStyle_orientation', 'vertical'); var showBranchOfficeRes = myGM.getValue('resourceInfo_showBranchRes', true); // If the info containers are not set, set them. if(!myGM.$(prefix + 'capacityInfowood')) { this.init(); } // Set all resource types. var types = new Array('wood', 'wine', 'marble', 'glass', 'sulfur'); // Get some needed data. var warehouseCapacity = new Array(); var branchOfficeResources = new Array(); var currentResources = new Array(); warehouseCapacity.push(ika.model.maxResources.resource); branchOfficeResources.push(ika.model.branchOfficeResources.resource); currentResources.push(ika.model.currentResources.resource); for(var i = 1; i < types.length; i++) { warehouseCapacity.push(ika.model.maxResources[i]); branchOfficeResources.push(ika.model.branchOfficeResources[i]); currentResources.push(ika.model.currentResources[i]); } // Set the styles for all resources. for(var i = 0; i < types.length; i++) { var maxCapacity = myGM.$(prefix + 'maxCapacity' + types[i]); var curWarehouse = myGM.$(prefix + 'warehouseCapacity' + types[i]); var curResource = myGM.$(prefix + 'currentResource' + types[i]); var curTmp = (typeof(currentResources[i]) == 'string') ? General.getInt(currentResources[i]) : currentResources[i]; var capTmp = (typeof(warehouseCapacity[i]) == 'string') ? General.getInt(warehouseCapacity[i]) : warehouseCapacity[i]; var offTmp = (typeof(branchOfficeResources[i]) == 'string') ? General.getInt(branchOfficeResources[i]) : branchOfficeResources[i]; var maxTmp = capTmp + offTmp; var curWarehousePercentage = showBranchOfficeRes ? (capTmp / maxTmp * 100) : 100; var curResourcePercentage = showBranchOfficeRes ? (curTmp / maxTmp * 100) : (curTmp / capTmp * 100); if(orientation == 'vertical') { maxCapacity.style.height = '100%'; curWarehouse.style.height = curWarehousePercentage + '%'; curResource.style.height = curResourcePercentage + '%'; } else { maxCapacity.style.width = '100%'; curWarehouse.style.width = curWarehousePercentage + '%'; curResource.style.width = curResourcePercentage + '%'; } } }, /** * Adds links to access town hall, tradegood and sawmill to the resources menu. */ addRessourceLinks: function() { // Set all tradegood types. var types = new Array('wine', 'marble', 'glass', 'sulfur'); // Get the population and wood link. var population = myGM.$('#resources_population'); var wood = myGM.$('#resources_wood'); // Add the event listener. population.addEventListener('click', function() { win.ajaxHandlerCall('?view=city&dialog=townHall&cityId=' + ika.getModel().relatedCityData[ika.getModel().relatedCityData.selectedCity].id + '&position=0'); }, true); wood.addEventListener('click', function() { win.ajaxHandlerCall('?view=island&dialog=resource'); }, true); // Add the event listener to the tradegood links. for(var i = 0; i < types.length; i++) { var tradegood = myGM.$('#resources_' + types[i]); tradegood.addEventListener('click', function() { win.ajaxHandlerCall('?view=island&dialog=tradegood'); }, true); } // Add the styles. myGM.addStyle( "#resources_population:hover, #resources_wood:hover, #resources_wine:hover, #resources_marble:hover, #resources_glass:hover, #resources_sulfur:hover { text-shadow: 2px 2px 2px #666; cursor: pointer; color: #333; }", 'resourceLinks', true); } }; /** * Functions for the member info. */ MemberInfo = { /** * Storage for the highscore type. */ type: '', /** * Storage for all actual member information. */ data: null, /** * Init the member info. */ init: function() { // Add the styles. myGM.addStyle( "#" + myGM.prefix + "resetInfo { float: right; margin-top: -6px; margin-right: 6px; } \ .highscore .score span { float: right; text-align: right; width: 70px; } \ .highscore .place span { float: right; text-align: right; width: 30px; } \ .highscore th:nth-child(4) { width: 30% !important; } \ .highscore th:nth-child(5) { width: 10% !important; } \ #tab_highscore .centerButton { margin: 10px 0px; }", 'memberInfo', true ); }, /** * Starts the script. */ show: function() { // Show the link for the info. this.addShowButton(); // Show the info if the flag is set. if(myGM.getValue('memberInfo_infoLinkClicked', false)) { // Delete the flag. myGM.setValue('memberInfo_infoLinkClicked', false); // Show the information. this.showInfo(); } }, /** * Add a button for showing the inforation to the highscore. */ addShowButton: function() { // Get the parent of the show button. var parent = myGM.$('#tab_highscore .centerButton'); // Add the button to show the information. var btn = myGM.addElement('input', parent, 'showInfo', 'button'); btn.type = 'button'; btn.value = Language.$('memberInfo_show'); // Add the event listener to the button. btn.addEventListener('click', EventHandling.memberInfo.clickShow, false); }, /** * Add a button for resetting the inforation to the highscore. */ addResetButton: function() { // Get the parent of the reset button. var parent = myGM.$('#tab_highscore .content p'); // Add the reset button. var btn = myGM.addElement('input', parent, 'resetInfo', 'button'); btn.type = 'button'; btn.value = Language.$('memberInfo_reset'); // Add the event listener to the button. btn.addEventListener('click', EventHandling.memberInfo.clickReset, false); }, /** * Adds the time since the last reset to the highscore view. */ addLastResetTime: function() { // Get the time since the last reset. var lastResetTime = myGM.getValue(General.getServerCode() + '_memberInfo_time_' + MemberInfo.type, 0); var diff = new Date() - lastResetTime; // Get the hours, days and minutes since the last reset. var d = Math.floor(diff / 86400000); diff = diff % 86400000; var h = Math.floor(diff / 3600000); diff = diff % 3600000; var m = Math.floor(diff / 60000); // Get the string of the last reset. var lastReset = lastResetTime ? (d + 'd ' + h + 'h ' + m + 'min') : Language.$('memberInfo_noReset'); // Get the parent of the reset button. var parent = myGM.$('#tab_highscore .content p'); // Add the reset button. var span = myGM.addElement('span', parent); span.innerHTML = '
' + Language.$('memberInfo_lastReset') + ' ' + lastReset + ''; }, /** * Shows the information about the members in the highscore view. */ showInfo: function() { // Set the highscore type. this.type = General.getSelectValue('js_highscoreType', true); // Storage for member information. var memberInfoData = {}; // Storage for old member information. var memberInfoDataOld = myGM.getValue(General.getServerCode() + '_memberInfo_data_' + this.type, null); // Get the table rows of the ally members. var allyRows = myGM.$$('table.highscore tr.ownally'); // Loop over all ally rows. for(var i = 0; i < allyRows.length; i++) { // Get the id of the ally member. var actionLink = myGM.$('.action a', allyRows[i]).href; var id = actionLink.match(/receiverId=([0-9]*)/i)[1]; // Get score and place of the member and store it. memberInfoData[id] = {}; memberInfoData[id]['place'] = General.getInt(myGM.$('.place', allyRows[i]).innerHTML); memberInfoData[id]['score'] = General.getInt(myGM.$('.score', allyRows[i]).innerHTML); // Get the difference in place and score. var placeDiff = myGM.addElement('span', myGM.$('.place', allyRows[i])); var scoreDiff = myGM.addElement('span', myGM.$('.score', allyRows[i])); // Show the difference in place and score. placeDiff.innerHTML += (memberInfoDataOld && memberInfoDataOld[id]) ? General.formatToIkaNumber(memberInfoDataOld[id]['place'] - memberInfoData[id]['place'], true, true) : '-'; scoreDiff.innerHTML += (memberInfoDataOld && memberInfoDataOld[id]) ? General.formatToIkaNumber(memberInfoData[id]['score'] - memberInfoDataOld[id]['score'], true, true) : '-'; } // Get the own table row. var ownRow = myGM.$('table.highscore tr.own'); // Get the own score and place. memberInfoData['own'] = {}; memberInfoData['own']['place'] = General.getInt(myGM.$('.place', ownRow).innerHTML); memberInfoData['own']['score'] = General.getInt(myGM.$('.score', ownRow).innerHTML); // Get the difference in place and score. var placeDiff = myGM.addElement('span', myGM.$('.place', ownRow)); var scoreDiff = myGM.addElement('span', myGM.$('.score', ownRow)); // Show the difference in place and score. placeDiff.innerHTML += (memberInfoDataOld && memberInfoDataOld['own']) ? General.formatToIkaNumber(memberInfoDataOld['own']['place'] - memberInfoData['own']['place'], true, true) : '-'; scoreDiff.innerHTML += (memberInfoDataOld && memberInfoDataOld['own']) ? General.formatToIkaNumber(memberInfoData['own']['score'] - memberInfoDataOld['own']['score'], true, true) : '-'; // Store the new member information for later use. this.data = memberInfoData; // Add the reset button. this.addResetButton(); // Show the time of the last reset. this.addLastResetTime(); } }; /** * Functions for messages. */ Message = { /** * Add a signature to each message. */ addSignature: function() { // Storage for the signature text. var signature = ''; // If the server signature should be used, get the server signature text. if(myGM.getValue('messageSignature_useServerSignature_' + General.getServerCode(), false)) { signature = myGM.getValue('messageSignature_serverSignature_' + General.getServerCode(), ''); // Otherwise: get the global signature text. } else { signature = myGM.getValue('messageSignature_globalSignature', ''); } // If a signature text is set, add it to the message. if(signature != '') { // Get the message area. var messageArea = myGM.$('#js_msgTextConfirm'); // Get the message text and add the signature on the right placement. var text = messageArea.value; text = myGM.getValue('messageSignature_placementTop', true) ? ('\n\n' + signature + text) : (text + '\n\n' + signature); messageArea.value = text; // Focus the message area on top. messageArea.setSelectionRange(0,0); messageArea.focus(); } }, /** * Add a link for circular messages to the toolbar. */ easyCircularMessage: function() { // Get the script toolbar. var scriptToolbar = myGM.$('#' + myGM.prefix + 'toolbar'); // Add the message link wrapper. var circularMessageLinkWrapper = myGM.addElement('div', scriptToolbar, 'circularMessageLinkWrapper'); // Prepare the message id, link, class and title. var id = myGM.prefix + 'circularMessageLink'; var href = '?view=sendIKMessage&msgType=51&allyId=' + ika.getModel().avatarAllyId; var cl = href.match(/diplomacyAlly/) ? 'notSet' : ''; var title = href.match(/diplomacyAlly/) ? Language.$('easyCircularMsg_getLink') : Language.$('easyCircularMsg_send'); // Add the message link (workaround for ajaxHandlerCall). circularMessageLinkWrapper.innerHTML = ''; // Set the styles. myGM.addStyle( "#" + myGM.prefix + "circularMessageLinkWrapper { position: absolute; top: 5px; right: 70px; } \ #" + myGM.prefix + "circularMessageLink { height: 9px; width: 13px; display: block; margin: 0px !important; background: url('skin/interface/icon_send_message.png') repeat scroll 0 0 transparent; } \ #" + myGM.prefix + "circularMessageLink:hover { background-position: 0px -9px; } \ #" + myGM.prefix + "circularMessageLink.notSet:hover { background-position: 0px -18px; }", 'easyCircularMessage', true); }, /** * Set the style for the replaced urls. */ setStyleForReplaceUrl: function() { // Add the style. myGM.addStyle( "." + myGM.prefix + "replacedUrl { font-weight: bold; font-style: italic; } \ ." + myGM.prefix + "replacedUrl:hover { text-decoration: underline; cursor: pointer; }" ); }, /** * Make all URLs in messsages clickable. But with a security check. */ replaceUrl: function() { // Get all message texts. var msgTexts = myGM.$$('.msgText'); // Loop over all message texts. for(var i = 0; i < msgTexts.length; i++) { // Replace the URLs. var text = msgTexts[i].innerHTML; msgTexts[i].innerHTML = text.replace(/(?:^|\s)(http(s?)\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(\/[^<\s]*)?)/g, ' $1 '); } // Get all replaced URLs. var replacedUrls = myGM.$$('.' + myGM.prefix + 'replacedUrl'); // Loop over all replaced URLs. for(var i = 0; i < replacedUrls.length; i++) { // Add a click event to each replaced URL. replacedUrls[i].addEventListener('click', EventHandling.replacedUrl.click, true); } } }; /** * Functions for cities. */ City = { /** * Set the links for cities that are colonizing. */ setColonizingLinks: function() { // Get all cities with level 0 (colonizing cities). var colonizingCities = myGM.$$('.level0'); // Loop over the colonizing cities. for(var i = 0; i < colonizingCities.length; i++) { // Get the location id. var locationId = colonizingCities[i].id.replace(/\D/g, ''); // Get the city id. var cityId = ika.getScreen().data.cities[locationId].id; // Set the city link. myGM.$('#js_cityLocation' + locationId + 'Link').href = '?view=cityDetails&destinationCityId=' + cityId; } } }; /** * Functions for nice formatting of units. */ UnitInfo = { /** * Add a link to the troops in town view to get a list for sharing. */ addPopupLink: function() { var infoButton = myGM.addElement('input', myGM.$('#cityMilitary_c .buildingDescription'), null, 'button', new Array(['position', 'absolute'], ['top', '5px'], ['right', '20px'])); infoButton.type = 'button'; infoButton.value = Language.$('unitInfo_button'); infoButton.addEventListener('click', EventHandling.unitInfo.click, true); }, /** * Shows a popup with the unit information. */ showPopup: function() { // Get information about the town. var selected = ika.getModel().relatedCityData[ika.getModel().relatedCityData.selectedCity]; var townInfo = selected.name + ' ' + selected.coords; // Storage for output. var output = ''; // Prepare the output. var text = { header: Language.$('unitInfo_header') + townInfo, body: output }; // Show the output. var notificationId = myGM.notification(text); // Set the style for the textarea. myGM.addStyle( "#" + myGM.prefix + "notificationPanelBodyMContent" + notificationId + " textarea { width: 100%; height: 260px; resize: none;", 'unitInfoTextarea', true ); // Add a the listener to select the information on focus. myGM.$('#' + myGM.prefix + 'notificationPanelBodyMContent' + notificationId + ' textarea').addEventListener('focus', function() { this.select(); }, false); }, /** * Gets the units in a wrapper and returns them as a list. * * @param String id * The id of the tab. * * @return String * The value for this tab. */ getOutput: function(id) { // Get the own units and store them. var ret = this.getOwnUnits(id); // Status of foreign troops. var foreignStatus = new Array('friends', 'enemies'); // Get all wrapper for troops. var wrapper = myGM.$$('#tab' + id + ' .contentBox01h'); // Loop over all wrappers. for(var i = 0; i < foreignStatus.length; i++) { // Get the foreign units for this status and store them. var foreignUnits = this.getForeignUnits(wrapper[i + 1], id, foreignStatus[i]); ret += (foreignUnits.length > 0) ? (((ret.length > 0) ? '\n' : '') + foreignUnits) : ''; } // Return the output. return ret; }, /** * Get the own units in the town. * * @param String id * The id of the tab. * * @return String * The list of own units. */ getOwnUnits: function(id) { // Get the wrapper. var wrapper = myGM.$('#tab' + id + ' .contentBox01h'); // Get the name and number cells. var nameCells = myGM.$$('.table01 .title_img_row th', wrapper); var numberCells = myGM.$$('.table01 .count td', wrapper); // Storage for the list. var list = ''; // Add the units if their numer is not zero. for(var i = 0; i < nameCells.length; i++) { var name = nameCells[i].title; var number = General.getInt(numberCells[i].innerHTML); list += (number > 0) ? '\n' + name + ': ' + number : ''; } // Get the headline. var headline = '\n--- ' + Language.$('unitInfo_' + id.toLowerCase() + '_own') + ' ---'; // Return the list with headline if some units were found. Otherwise return an empty string. return (list.length > 0) ? (headline + list) : ''; }, /** * Get the foreign units in the town with the status. * * @param String wrapper * The wrapper which contains the foreign units. * @param String id * The id of the tab. * @param String status * The status of the units. * * @return String * The list of foreign units. */ getForeignUnits: function(wrapper, id, status) { // Get the name cells and number rows. var nameCells = myGM.$$('.table01 .title_img_row th:not(:first-child)', wrapper); var numberRows = myGM.$$('.table01 tr:not(.title_img_row)', wrapper); // Distance between two belonging rows and storage for return value. var distance = numberRows.length / 2; var ret = ''; // Iterate over all number rows. for(var i = 0; i < distance; i++) { // Get the number cells and the player name. var numberCells = myGM.$$('td:not(:first-child)', numberRows[i]).concat(myGM.$$('td:not(:first-child)', numberRows[i + distance])); var playerName = myGM.$('td a', numberRows[i]).innerHTML; // Storage for the list. var list = ''; // Add the units if their numer is not zero. for(var k = 0; k < numberCells.length; k++) { var name = nameCells[k].title; var number = General.getInt(numberCells[k].innerHTML); list += (number > 0) ? '\n' + name + ': ' + number : ''; } // Add the units with the player name to the list, if some units were found. list = ((list.length > 0) ? '\n* ' + playerName + ' *' : '') + list; ret += ((list.length > 0 && ret.length > 0) ? '\n' : '') + list; } // Get the headline. var headline = '\n--- ' + Language.$('unitInfo_' + id.toLowerCase() + '_' + status) + ' ---'; // Return the list with headline if some units were found. Otherwise return an empty string. return (ret.length > 0) ? (headline + ret) : ''; } }; /** * The main function of the script. */ function main() { // Init the myGM functions. myGM.init(); // If the script was already executed, do nothing. if(myGM.$('#' + myGM.prefix + 'alreadyExecutedScript')) return; // Add the hint, that the script was already executed. var alreadyExecuted = myGM.addElement('input', myGM.$('#container'), 'alreadyExecutedScript'); alreadyExecuted.type = 'hidden'; // Init the language. Language.init(); // Init the script. General.init(); // Call the function to check for updates. Updater.init(); // Call the function to enhance the view. EnhancedView.init(); } // Call the main function of the script. setTimeout(main, 0);