// ==UserScript== // @author McBen, Vashiru, j00rtje, DanielOnDiordna // @name Ultimate Mission Maker - Extended // @id ummex@McBen // @category Mission // @version 1.1.2 // @namespace https://github.com/IITC-CE/ingress-intel-total-conversion // @updateURL https://raw.githubusercontent.com/IITC-CE/Community-plugins/master/dist/McBen/ummex.meta.js // @downloadURL https://raw.githubusercontent.com/IITC-CE/Community-plugins/master/dist/McBen/ummex.user.js // @description Easily create mission banners (fan update of Ultimate Mission Maker) // @icon64 data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAA2xSURBVHic3Zt5cJ3VecZ/3/3urrtfyZK1WF4kS5YsSw4QswbGmBBDCI4JbUJpyzAQmGba0jS0TOiStklaQgJkUloomaZpghmGYYgxNkmcmIFaGDtEkq3Fi2RL8qJdutLV3b+tf3y+17rSlaUrXUltnhmPR0dnec+j8z7nPee8H6ws3Cs8/oqhGPhvQAU+BD65suYsH6zA00AI0Kb8U4EfoxPzO4v7gW4uT7qwsFDbvn27VlFRoQmCkCRiEvg6OlHLAmEZxtgKvAB8CsDhcFBXV0dZWVmqwuTkJC0tLfT39yeLLqKvlJ+gE7NkWEoC/MDfAV8BRKPRSFVVFZs2bcJgMGRsMDg4SHNzM8FgMFl0FPjzy/8vCZaCABPwJ8A/AG5BEFizZg319fVYrVdWdqxgC6NVf4wp2EP+qR9jSOiTVlWVs2fP0tbWhiRJoOvDq8CTwGCujc01ATuA7wM1AH6/n4aGBvx+f6qCZF/N6OZHiTnXXTFClfB2v42z910ETQEgkUjQ3t5OV1cXmqaBLpzfA/4ZiOfK4FwRUAU8B9wFYLPZqKurY+3atakKqsnO+MYvMVl0IxqZXUCUgvjO7CFv4MqKDwQCtLS0MDw8nCzqQhfKN3Jh+GIJ8ALfQF/yRlEUqa6uprq6GlEUL49gIFi+k/F1n0M1mNMai0oUVbSiTTPDGuzG3/FDTOG+VFlfXx/Nzc2Ew+Fk0SHgCaB1MRNYKAFG4GHgm0ABQFlZGfX19djt9lSlWH4dI9UPI1s86YNqCq5L7+HpfB3ZXsho7ZeJOdZMG0Ijb+g3+E+/OkMfWltbkWUZQAb+E/gbYJgFYCEE3A48D9QB+Hw+GhoayM/PT1WQ7UWM1jxE1F01ramGffwk/taXERPBtN9Ei65ntOoPkI2OdANVCXfvAdw9+xFUSa8bjdLe3k53d3dSHwLAM5ftSmQzmWwIqAS+hR7QYLPZqK2tZd26dQiC3o0mWglU/h6TJbfO8HNLdBB/28uYg92zDqAhENywi4nyu1AFY9rvjIkJvJ2vpenD2NgYLS0tjIyMJItOA38J7J/vpOZDgAP4GvAUYBFFkcrKSmpqajAajSnDQ2XbCWy4H1W0pDUWlSi+06+S1984X5tQjDYCmx8h5N86w0TLZC/5HT/EFLqYKrtw4QLHjx8nEokki36Frg/tc411NQIMwIPAd4BCgOLiYrZu3UpeXl6qku7nDyFbfOkdT/FzQZXnsiMjJEfJvPVBURQ6Ozvp6OhI6oME/Dt6MDYx2xizEXAbevhaD+D1emloaKCgoCBVQc4rYnTTVfy87T8Q47OOmxUiRdsYq3pwFn3Yj7vnQJo+nDhxgt7e3mS1UeCfgH8FlOl9ZyLgu+h+hNVqTe3n8/Fzc2wIf+tLWK7i5wuFhkCw4j4m1tyZWR/OvEbe4BV9GB4epqWlhUAgkCw6CtyCvjJSyERAp9VqrSgtLaWurg6TyZQy4Kp+3rmHvEuHFznNuZGNPmiaRnd3N01NTaiqCrAGuDC1TUYCVq9eXbF+/Xr8fj9Wq5WYv46RTUvj5wuF5ChltPbRzPoweAz/mT0pfXjrrbeS54oZBBiZBaqqMjExwaRjA+GGr84YxDHaiq/9FQxSaPGzWQBMoYsUHf17IsU3MbrxARQxGYAJhAu3ITnLKD7y9Jz9zEpAEjJi2s+CpuDp3ou7e9+CDM817H2N2IaaGWx4gpi7MlWuTQu7Z8OcBEyFgIYmiATW7ybqq8V7eg+W0PnsLM4hNNFCqORWAuV3o5pdun1ZBrdZEYCmYv7gm8jXPkbMU0X/J79B3vDHeDtfxxgbzaqrxUATRMLFtxBYdy+KxQOaHhcIaIRWZXe/mh0BgHjpKOJAM3LVvcibv0h41XVE/HW4ew7gPv8LBHX+obhidiEoCQxKbF71NQQiq64lsOE+ZHshALaxDrxn38Ac7CFQ81C208meAACUBMaONxDPHUTa8ocoG+5kfMNuQqW34e16Q9+Ptdmv8iR7EcE1nyG0+kYEVcZ56T1cFw4ixsdnbRP11RCo+H0STl31LRNdeM++iTVwakFTSGJhBFyGEBvHfOwHaGf2IX3iy8hFDQzXPkaw7A58Z17DMtGVVj/u3kCwfCeRgk+gISBoCqpoZaL8LoJlnyZv8CPcve+m3QPEPRsJbNhNzKNHnObwJdzn9pI39JvFmJ7CoghIQhjvwXzo6yhFDcjXPEbcvZ7+a5/GPnIc75k9yLZ8Jss+TSS/HgCDEsPZ9z9ECrYixiewBM8xWfwpQqtvJlR0E7bASayjrcS91ak2xtgInp795F16HyGHF8U5ISAJcaAF8d0/Ra68G2nzA0Ty64n4t8DlMNoYG8V1/pc4+z5AUGJE/HUIqoTvzB7c3W8zWbaDydLbifpqiPpq9DbxMTzn9uLobwRtRii/aOSUAABUGePpvRh7DiFtfRR5/Q7E2Bi+c29iHziauvSEy2GooJ8nRCmE59zPcPe+S7DkNgKVX0RMBCn58KnUQWcpkPl2MheIT2LoOQSAZbyTvP4P0yYP6EIppO/bghLHMXBEN04KL+nkYSkJmA80NevAJddYFgK0WbdELeUCK4UVISDhKGV48+PIjhLizrWM1DyCbM2fpfXSIvcimAFJAuKu9Yyv/SzR/AZAxTHwEZpgIFR0I+HCbTj63sfT8w5o6nKYBSwTAarZyVD9E0Ty6xE0hbyBI3h69mGKDADgznubifW7mCy9ndDqm3EMfrQcZgHLRIBUUIesJnBdOIir9+cY42NpvzeH+yho/Tec7krGN+xmsvjW5TALWGINEORYajlrGsiKijDL0ziAJprBcOVvIkrBWevmCku6Agwjp7Due0Q/OVbsJLL2TiJrbsc+cBTv+QOpmH96vC9GR/BcPIjj4ntLaR6wDC4ghAYw/fZljCffRK7+PErFTiLFNxEtvhHbUBOKxU3cXaEbEx3C072PvIEjM4OmrAeeX3yxLBoAIERGMDW9grH9deSNn0Ou3kVk1TW6EbmceJZYNgKSEOJBTK0/xTjwMbE7nsMU7qfk6N8uyUFnPli5MEzWkzzExMSKTR5W+iywpJifBmRHgCCgWf/vZrcq094O54OsCNAwEN/1E+StD4NoynqwpULCWU7/Dd8mVHBN1m3nFsHQIAYlnnoP1AxGpE1fQKm4E9PHL2HoXvq9ejaoJgejmx8l7Ktj+pI3B8/Nq485V4Bh4jzmN7+EqfuXaYcU1eQkfsOTJO55BdVXeZUelgCCyETFF7hwy/OEfVuYOnmjFGTV8R9QcOLFeXWVaQVIkiQxNDSE3+/HZrMhyDGMR15APLEH+aYnkfNrU5UVZwnqZ76POHQc4+FnEGKzX23nApHCaxmt+iMUkzOtXFAlPL37cXe/k9pVQqEQLS0tyYdRmPY0Dkx7+NNxPhKJ3BqLxZySJCHLMlarFYPBgCCFEc8exDjcjlZYh2a6kimi5hUhV9+LYPMiDLQgzHWktXmRK+/GGBvRLzznQMJRyvDWrzJRegda2vO8Rt7wbyls+g620VZAQ5Ik2traOHbsWDLtNgr8I3BgPgScAV6SZVkOhULb4vG4KR6Po6oqVqsVQRAQQgOIp/dikKNoq2rRkgcYwYDq34hSdQ+G+ARC4OyiCVCNeYzWPcbYxgeQzenpdpbQBQqbn8V14dcY1ASaptHb20tjYyODg4PJe4h3gHuAtzP1P9dmWQp8WxTFB91ut+ByufB6vbhcrlQFzWhFue5xpLU7ZlxviZMXMTY+i2Gsc0bHmncdsZ0vYg2coqjpmQyWiUysu4fxtXehCek7jlGaxHfyv7APN6XKhoeHaW5uZnw85YLN6IlSH1xtgvO9kdwGvGA2m69PEuDz+dKSnzVHIfKNX0vTh+QAmfThagRk4+fZ5gRNRyYXyIRLwI8URTkXDodviEajjkQika4Picv6MNKBWrRlFn3wIAwc1/Uhgwtc3c+bKGx6JuXniqJw8uRJjhw5kswDkoAXgc8D7zPP7wzmSwCXOzyOrg9SKBS6IZFIGDPpg/H0XgxyLIM+VKFs/CyGeBAhOpYiwD7SMrufhy9S2PQsrou6n4OeF3j48GH6+vqSfv4rYBf6BxZZZZIv5lK+Al0f7ne73Xg8HjweD07nlGVrsiFd9xXk8ttm6IMxMohsL8QUG0W2uNGmZ35JE/hP/gjb8PFUWYbM0FPoGW0z1H2+yMWrxHbgebPZvCWpD36/H4tlyhJ2lSDd/FfInrkDJoMq4e7+Ge7en6cCrwy5wWPo29qL6AnTC0aunmWSWaXP2u32VR6PB7fbjd/vv5I2D6gl25C2/Rmq1TvTEE3FMfQR3tOvYpD0lNcM2Z+Lzg6fMW4uOpkCL/DXgiD8hdPpNLvdbrxeLx6PJ5VoiSCgbNqNXPdg6nxhCV/E3/oS5vClVEcZvg/4Nfq21pZLg5fqYW4j8Jwoincn9cHr9eJwTDmummxo1z2Oa6IN+8iJVHGGL0Q60b8gy8kXItOx1C+TO9Djh9pM+mA2m/F6dXeIx+N0dHRM/UZoHPgX9JzlnH0jNB3ZbIMLwTngFUVRRsPh8PWxWMw6dds0m82YzWa6urpobGxM/tVV4KfAvcAvmEcw8/8FBcDLgiDILpdLKy8v12prazWHwzH1E9pDXM5Q/11GPfCe0WjUnE5ncuJngd0ra9by4z70v/hTLOO3wtPxv8EA3YCUDCjNAAAAAElFTkSuQmCC // @homepageURL https://github.com/IITCPlugins/umm_ext // @issueTracker https://github.com/IITCPlugins/umm_ext/issues // @recommends draw-tools@breunigs // @match https://intel.ingress.com/* // @match https://missions.ingress.com/* // ==/UserScript== /** * # v1.1.1 * * - fix: "edit" button was covering banner length in main dialog * - dependencies update * * # v1.1 * * - new "Mission Generator" dialog * This new dialog provides several tools to modify current mission: * 1. "Reset" * Discard all current changes. * 2. Add portals * Adds nearby portals to the current mission. * You can: * - Limit selection using a DrawTools polygon * - Exclude individual portals with DrawTool Markers * - Restrict selection to portals within path hack range * 3. Sort portals * Attempts to arrange portals for the shortest possible path. * (Note: This is a complex optimization problem—results may vary. * The “keep end portal” option may occasionally fail.) * 4. Change start * Set the selected Portal as new mission start. * If no portal is selected, the start point will cycle through all mission portals. * * All changes are temporary until "applied" or be "dismissed". * Note: Distance calculations are based on straight-line (“as-the-crow-flies”) distances; real-world paths are not considered. * * - Use static layers * UMM is now fully hidden when inactive. Background processing is also disabled while inactive. * - Added Multi-Reverse * Using the reverse action in the main dialog, you can now reverse an entire banner or selected parts of it—not just a single mission. * - Drag: allow swapping mission portals * - Fixed merge in main dialog * - Fixed “Should merge?” prompt in split option (main dialog) * - Mission-Select dialog moved to the left * * # v1.0.2 * * - fix IITC-Button load * in iitc-button load order is differnet and custom "if UUM is loaded then disable it" failed * - fix variable if both plugins are active * * # v1.0.1 * * - fix mission number (index started by 0 instead of 1) * * # v1.0 * * This is a complete rewrite of the Ultimate Mission Maker from a developer perspective. * The entire codebase has been redesigned while maintaining the familiar user experience of the original UMM. * Below are the visible improvements and changes you'll notice. * * ## What's Changed: * * - UMM is now hidden by default. You need to hit the "UMM" button in the Portal details window to make it appear. * * - **Select Mission Dialog** (open it through the toolbar or the main dialog) * - Selecting a mission is no longer required; simply open another mission * - Navigation buttons (+/-) allow you to cycle through missions * - Added split, clear, merge, and reverse commands for mission manipulation * - New mission information display: portal count and distances * * - **Banner Settins** (start window) * - changed Title placeholders to $T $M $N * - **Option Dialog** (main window) * - Banner information now displays as a compact table * - Removed warning for mission counts that are not multiples of 6 * - Added warning when missions lack sufficient waypoints * * - **Drag & Drop** in the mission editor path * - Move existing markers to adjust waypoints * - Add new waypoints by positioning intermediate markers at new locations * - Remove waypoints by double-clicking a marker * - Merge missions by dragging start and end markers together * * - **Mission Numbers** * - Potential split points are previewed while creating missions * * - **Waypoint edit** * - current mission is preselected * - passphrases: add random default questions. * when question & answer is empty a simple question will be set. * * - **Miscellaneous** * - Custom confirmation dialogs clarify actions and improve readability * - Switch between any missions, even those without portals * - Option to split missions when starting on a portal that's already assigned to another mission * - on mobile dialogs are not at the top instead of centered * - flash buttonbar on activation to draw attention * * --- * * # History: * * ## v1.0.beta.2 - 15.02.26 * * - fixed update-URL in script header * * ## v1.0.beta - 15.02.26 * * - first public release * - automated build process on GitHub * - fixed layer checkboxes in Option-Dialog * - add "clear" mission to selection dialog * - always color selected mission even when not in Edit-Mode * - move "no" to left in custom confirm dialog * - remove doubled "v" in version numbers * - fix toggeling edit mode on mission detail window "save" button * - close dialog on mission detail window "save" * - fix linebreaks in changelog dialog * - select mission: directly select mission on combo-box change * - fix question text in portal details * - on mobile dialogs are not at the top instead of centered * */ function wrapper_iitc(SCRIPT_INFO) { (() => { "use strict"; var __webpack_modules__ = { 707(module, __webpack_exports__, __webpack_require__) { var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(601), _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default = __webpack_require__.n(_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__), _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(314), ___CSS_LOADER_EXPORT___ = __webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__)()(_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default()); ___CSS_LOADER_EXPORT___.push([ module.id, "#umm-mission-picker-info{border:1px solid #ff0;border-radius:2px;margin:1em 4px 4px}.umm-mission-btn{margin-bottom:2px;margin-right:1em;margin-top:2px}.umm-mission-btn.w-full{width:90%}.umm-notification{background-color:#383838;border-radius:2px;-webkit-box-shadow:0 0 24px -1px #383838;-moz-box-shadow:0 0 24px -1px #383838;box-shadow:0 0 24px -1px #383838;color:#f0f0f0;font-family:Calibri,sans-serif;font-size:20px;height:auto;left:50%;margin-left:-100px;padding:10px;position:absolute;text-align:center;top:20px;width:300px;z-index:10000}.umm-options-list{overflow:hidden}.umm-options-list .banner_info{border:1px solid grey;border-radius:2px;margin-top:0;min-height:7.1em;padding:4px;position:relative}.umm-options-list .banner_info .title{font:700;font-size:large}.umm-options-list .banner_info .description{font-size:small;margin-bottom:.5em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.umm-options-list .banner_info .stat{font-weight:700;margin-left:.4em}.umm-options-list .banner_info #umm_opt_error{padding-top:.5em}.umm-options-list .banner_info #umm_opt_error .error{color:#ffa0a0;font-weight:700}.umm-options-list .banner_info .editButtom{bottom:0;position:absolute;right:0}.umm-options-list a{background:rgba(8,48,78,.9);border:1px solid #ffce00;color:#ffce00;display:block;margin:10px auto;padding:3px 0;text-align:center;width:80%}#dialog-umm-edit-mission-set-details{background-color:rgba(8,48,78,.9)}.umm-edit-mission-set-details p{display:block;margin:5px 0 8px}.umm-edit-mission-set-details label{display:block;margin-bottom:5px}.umm-edit-mission-set-details input,.umm-edit-mission-set-details textarea{margin-bottom:15px;width:100%}.umm-edit-mission-set-details textarea{background-color:#062844;color:#ffce00;font-family:inherit;resize:vertical;width:calc(100% - 6px)}.umm-edit-mission-set-details span.umm-error{color:#fff;display:block;display:none;margin-bottom:5px}.umm-edit-mission-set-details span.umm-error b{color:red}.umm-dialog-button{margin-left:5px}.umm-mission-marker .start{fill:#16d4b2;stroke:#005243;stroke-miterlimit:10}.umm-mission-marker .active{fill:#6832da;stroke:#16043f;stroke-miterlimit:10}.umm-mission-number{color:#000;font-family:monospace;font-size:14px;font-weight:700;left:0;position:absolute;text-align:center;top:6px;width:34px}#umm-waypoint-editor{border-bottom:1px solid #20a8b1;border-top:1px solid #20a8b1;box-sizing:border-box;color:#ffce00;display:flex;flex-direction:column;margin-bottom:10px;margin-top:10px;padding:8px 5px;width:100%}.umm-waypoint-editor-title{font-weight:700;margin-bottom:6px}.umm-waypoint-select-container{display:flex;flex-direction:row;width:100%}.umm-waypoint-select-container>select{background-color:#062844;border:none;color:#ffce00;height:24px}#umm-mission-select{width:60px}#umm-action-select{margin-left:4px;width:100%}#umm-passphrase-container{display:none;flex-direction:column;margin-top:5px}#umm-passphrase-container>span{margin-bottom:3px}#umm-passphrase-container>input,#umm-passphrase-container>textarea{background-color:#062844;border:none;color:#ffce00;font-family:Arial;margin-bottom:5px;min-height:24px;padding:3px;resize:vertical}.umm-confirm.no_title .ui-dialog-titlebar{display:none}.umm-confirm .header{font-size:1.4em;line-height:1.4em;margin:10px;overflow:hidden;text-align:center;text-overflow:ellipsis}.umm-confirm .details{color:#ccc;text-align:center}.um-helpTooltip{cursor:help;font-size:small;margin-left:5px}", "" ]); const __WEBPACK_DEFAULT_EXPORT__ = ___CSS_LOADER_EXPORT___; __webpack_require__.d(__webpack_exports__, [ "A", 0, __WEBPACK_DEFAULT_EXPORT__ ]); }, 314(module) { module.exports = function(cssWithMappingToString) { var list = []; return list.toString = function toString() { return this.map(function(item) { var content = "", needLayer = void 0 !== item[5]; return item[4] && (content += "@supports (".concat(item[4], ") {")), item[2] && (content += "@media ".concat(item[2], " {")), needLayer && (content += "@layer".concat(item[5].length > 0 ? " ".concat(item[5]) : "", " {")), content += cssWithMappingToString(item), needLayer && (content += "}"), item[2] && (content += "}"), item[4] && (content += "}"), content; }).join(""); }, list.i = function i(modules, media, dedupe, supports, layer) { "string" == typeof modules && (modules = [ [ null, modules, void 0 ] ]); var alreadyImportedModules = {}; if (dedupe) for (var k = 0; k < this.length; k++) { var id = this[k][0]; null != id && (alreadyImportedModules[id] = !0); } for (var _k = 0; _k < modules.length; _k++) { var item = [].concat(modules[_k]); dedupe && alreadyImportedModules[item[0]] || (void 0 !== layer && (void 0 === item[5] || (item[1] = "@layer".concat(item[5].length > 0 ? " ".concat(item[5]) : "", " {").concat(item[1], "}")), item[5] = layer), media && (item[2] ? (item[1] = "@media ".concat(item[2], " {").concat(item[1], "}"), item[2] = media) : item[2] = media), supports && (item[4] ? (item[1] = "@supports (".concat(item[4], ") {").concat(item[1], "}"), item[4] = supports) : item[4] = "".concat(supports)), list.push(item)); } }, list; }; }, 601(module) { module.exports = function(i) { return i[1]; }; }, 340(module, __webpack_exports__, __webpack_require__) { __webpack_require__.r(__webpack_exports__), __webpack_require__.d(__webpack_exports__, { default: () => src_styles }); var injectStylesIntoStyleTag_namespaceObject = __webpack_require__.cjs(function(module, exports) { var stylesInDOM = []; function getIndexByIdentifier(identifier) { for (var result = -1, i = 0; i < stylesInDOM.length; i++) if (stylesInDOM[i].identifier === identifier) { result = i; break; } return result; } function modulesToDom(list, options) { for (var idCountMap = {}, identifiers = [], i = 0; i < list.length; i++) { var item = list[i], id = options.base ? item[0] + options.base : item[0], count = idCountMap[id] || 0, identifier = "".concat(id, " ").concat(count); idCountMap[id] = count + 1; var indexByIdentifier = getIndexByIdentifier(identifier), obj = { css: item[1], media: item[2], sourceMap: item[3], supports: item[4], layer: item[5] }; if (-1 !== indexByIdentifier) stylesInDOM[indexByIdentifier].references++, stylesInDOM[indexByIdentifier].updater(obj); else { var updater = addElementStyle(obj, options); options.byIndex = i, stylesInDOM.splice(i, 0, { identifier, updater, references: 1 }); } identifiers.push(identifier); } return identifiers; } function addElementStyle(obj, options) { var api = options.domAPI(options); api.update(obj); return function updater(newObj) { if (newObj) { if (newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap && newObj.supports === obj.supports && newObj.layer === obj.layer) return; api.update(obj = newObj); } else api.remove(); }; } module.exports = function(list, options) { var lastIdentifiers = modulesToDom(list = list || [], options = options || {}); return function update(newList) { newList = newList || []; for (var i = 0; i < lastIdentifiers.length; i++) { var index = getIndexByIdentifier(lastIdentifiers[i]); stylesInDOM[index].references--; } for (var newLastIdentifiers = modulesToDom(newList, options), _i = 0; _i < lastIdentifiers.length; _i++) { var _index = getIndexByIdentifier(lastIdentifiers[_i]); 0 === stylesInDOM[_index].references && (stylesInDOM[_index].updater(), stylesInDOM.splice(_index, 1)); } lastIdentifiers = newLastIdentifiers; }; }; }), injectStylesIntoStyleTag_default = __webpack_require__.n(injectStylesIntoStyleTag_namespaceObject), styleDomAPI_namespaceObject = __webpack_require__.cjs(function(module, exports) { module.exports = function domAPI(options) { if ("undefined" == typeof document) return { update: function update() {}, remove: function remove() {} }; var styleElement = options.insertStyleElement(options); return { update: function update(obj) { !function apply(styleElement, options, obj) { var css = ""; obj.supports && (css += "@supports (".concat(obj.supports, ") {")), obj.media && (css += "@media ".concat(obj.media, " {")); var needLayer = void 0 !== obj.layer; needLayer && (css += "@layer".concat(obj.layer.length > 0 ? " ".concat(obj.layer) : "", " {")), css += obj.css, needLayer && (css += "}"), obj.media && (css += "}"), obj.supports && (css += "}"); var sourceMap = obj.sourceMap; sourceMap && "undefined" != typeof btoa && (css += "\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))), " */")), options.styleTagTransform(css, styleElement, options.options); }(styleElement, options, obj); }, remove: function remove() { !function removeStyleElement(styleElement) { if (null === styleElement.parentNode) return !1; styleElement.parentNode.removeChild(styleElement); }(styleElement); } }; }; }), styleDomAPI_default = __webpack_require__.n(styleDomAPI_namespaceObject), insertBySelector_namespaceObject = __webpack_require__.cjs(function(module, exports) { var memo = {}; module.exports = function insertBySelector(insert, style) { var target = function getTarget(target) { if (void 0 === memo[target]) { var styleTarget = document.querySelector(target); if (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) try { styleTarget = styleTarget.contentDocument.head; } catch (e) { styleTarget = null; } memo[target] = styleTarget; } return memo[target]; }(insert); if (!target) throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid."); target.appendChild(style); }; }), insertBySelector_default = __webpack_require__.n(insertBySelector_namespaceObject), setAttributesWithoutAttributes_namespaceObject = __webpack_require__.cjs(function(module, exports) { module.exports = function setAttributesWithoutAttributes(styleElement) { var nonce = __webpack_require__.nc; nonce && styleElement.setAttribute("nonce", nonce); }; }), setAttributesWithoutAttributes_default = __webpack_require__.n(setAttributesWithoutAttributes_namespaceObject), insertStyleElement_namespaceObject = __webpack_require__.cjs(function(module, exports) { module.exports = function insertStyleElement(options) { var element = document.createElement("style"); return options.setAttributes(element, options.attributes), options.insert(element, options.options), element; }; }), insertStyleElement_default = __webpack_require__.n(insertStyleElement_namespaceObject), styleTagTransform_namespaceObject = __webpack_require__.cjs(function(module, exports) { module.exports = function styleTagTransform(css, styleElement) { if (styleElement.styleSheet) styleElement.styleSheet.cssText = css; else { for (;styleElement.firstChild; ) styleElement.removeChild(styleElement.firstChild); styleElement.appendChild(document.createTextNode(css)); } }; }), styleTagTransform_default = __webpack_require__.n(styleTagTransform_namespaceObject), styles = __webpack_require__(707), options = {}; options.styleTagTransform = styleTagTransform_default(), options.setAttributes = setAttributesWithoutAttributes_default(), options.insert = insertBySelector_default().bind(null, "head"), options.domAPI = styleDomAPI_default(), options.insertStyleElement = insertStyleElement_default(); injectStylesIntoStyleTag_default()(styles.A, options); const src_styles = styles.A && styles.A.locals ? styles.A.locals : void 0; } }; const __webpack_module_cache__ = {}; function __webpack_require__(moduleId) { const cachedModule = __webpack_module_cache__[moduleId]; if (void 0 !== cachedModule) return cachedModule.exports; const module = __webpack_module_cache__[moduleId] = { id: moduleId, exports: {} }; return __webpack_modules__[moduleId](module, module.exports, __webpack_require__), module.exports; } __webpack_require__.n = module => { const getter = module && module.__esModule ? () => module.default : () => module; return __webpack_require__.d(getter, { a: getter }), getter; }, __webpack_require__.d = (exports, definition) => { if (Array.isArray(definition)) for (var i = 0; i < definition.length; ) { var key = definition[i++], binding = definition[i++]; __webpack_require__.o(exports, key) ? 0 === binding && i++ : 0 === binding ? Object.defineProperty(exports, key, { enumerable: !0, value: definition[i++] }) : Object.defineProperty(exports, key, { enumerable: !0, get: binding }); } else for (var key in definition) __webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key) && Object.defineProperty(exports, key, { enumerable: !0, get: definition[key] }); }, __webpack_require__.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop), __webpack_require__.r = exports => { Symbol.toStringTag && Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }), Object.defineProperty(exports, "__esModule", { value: !0 }); }, __webpack_require__.cjs = body => { const mod = { exports: {} }; return body.call(mod.exports, mod, mod.exports), mod.exports; }, __webpack_require__.nc = void 0; __webpack_require__.d({}, { i: () => main }); const debounce = (fct, timeout = 10) => { let timer; return (...data) => { clearTimeout(timer), timer = window.setTimeout(() => { fct.apply(void 0, data); }, timeout); }; }, confirmDialog = options => new Promise((resolve, _) => { const message = $("
"); message.append($("
", { class: "header" }).append(options.message)), options.details && message.append($("
"), $("
", { class: "details" }).append(options.details)); const buttons = [ { text: "No", click: () => { newDialog.dialog("close"), resolve(!1); } }, { text: "Yes", click: () => { newDialog.dialog("close"), resolve(!0); } } ], newDialog = window.dialog({ html: message, title: options.title, dialogClass: "umm-confirm " + (options.title ? "" : " no_title"), resizable: !1, modal: !0, closeOnEscape: !1, buttons }); newDialog.parent().find("button:eq(1)").css({ float: "left" }), newDialog.closest(".ui-dialog").trigger("focus"), newDialog.closest(".ui-dialog").on("keydown", event => "Enter" === event.key ? (event.preventDefault(), event.stopPropagation(), newDialog.parent().find("button:eq(2)").trigger("click"), !1) : "Escape" !== event.key || (event.preventDefault(), event.stopPropagation(), newDialog.parent().find("button:eq(1)").trigger("click"), !1)), newDialog.dialog("moveToTop"); }), bannerNotification = (state, message) => notification(`${state.getBannerName()}\n${message}`), notification = (notificationText, presistend = !1) => { $(".umm-notification").remove(), notificationText = notificationText.replace(/\n/g, "
"); const notification = $("
", { class: "umm-notification", html: notificationText }); $("body").append(notification), presistend || window.setTimeout(() => { $(".umm-notification").fadeOut(400, () => notification.remove()); }, 3e3); }; let icon; class DragMarker { editDragLine; mission; marker; layer; startLocation; constructor(layer, location, portalId, mission, isDummy = !1) { this.mission = mission, this.layer = layer, this.startLocation = location, this.marker = new L.Marker(location, { icon: (icon || (icon = L.Browser.touch ? new L.DivIcon({ iconSize: new L.Point(20, 20), className: "leaflet-div-icon leaflet-editing-icon leaflet-touch-icon" }) : new L.DivIcon({ iconSize: new L.Point(8, 8), className: "leaflet-div-icon leaflet-editing-icon" })), icon), draggable: !0, zIndexOffset: 7e3, opacity: isDummy ? .4 : 1, portal: portalId, isMidPoint: isDummy }), layer.addLayer(this.marker), this.marker.on("drag", event => { this.onMarkerDrag(event); }).on("dragstart", event => { this.onMarkerDragStart(event); }).on("dragend", event => { this.onMarkerDragEnd(event); }).on("dblclick", event => { this.onMarkerDblClick(event); }); } destroy() { this.layer.removeLayer(this.marker); } onMarkerDragStart(event) { const marker = event.target, options = event.target.options, isMidPoint = options.isMidPoint; this.editDragLine && this.layer.removeLayer(this.editDragLine); const portal = options.portal, portal_pre = portal > 0 ? this.mission.portals.get(portal - 1) : void 0, portal_post = this.mission.portals.get(portal + (isMidPoint ? 0 : 1)); let lls = [ portal_pre && new L.LatLng(portal_pre.location.latitude, portal_pre.location.longitude), marker.getLatLng(), portal_post && new L.LatLng(portal_post.location.latitude, portal_post.location.longitude) ]; portal_pre || portal_post ? portal_pre ? portal_post || (lls = [ lls[1], lls[0] ]) : lls.splice(0, 1) : lls = [ marker.getLatLng(), marker.getLatLng() ], this.editDragLine = new L.Polyline(lls, { color: "#ff9a00", weight: 3, dashArray: "5,5", pointerEvents: "none" }), this.layer.addLayer(this.editDragLine); } onMarkerDrag(event) { if (!this.editDragLine) return; const marker = event.target, snappedPortal = this.getSnapPortal(marker.getLatLng()), newTarget = snappedPortal ? snappedPortal.getLatLng() : marker.getLatLng(), latlngs = this.editDragLine.getLatLngs(); latlngs[3 === latlngs.length ? 1 : 0] = newTarget, this.editDragLine.setLatLngs(latlngs); } async onMarkerDragEnd(event) { this.editDragLine && (this.layer.removeLayer(this.editDragLine), this.editDragLine = void 0); const marker = event.target, options = event.target.options, snappedPortal = this.getSnapPortal(marker.getLatLng()); if (!snappedPortal) return void this.marker.setLatLng(this.startLocation); const oldPortalIndex = this.mission.portals.indexOf(snappedPortal.options.guid), portalToAdd = this.mission.portals.create(snappedPortal.options.guid); if (options.isMidPoint) -1 !== oldPortalIndex && (this.mission.portals.remove(oldPortalIndex), options.portal > oldPortalIndex && options.portal--), this.mission.portals.insert(options.portal, portalToAdd); else if (-1 === oldPortalIndex) await this.movePortal(options.portal, portalToAdd); else { const a = this.mission.portals.get(options.portal), b = this.mission.portals.get(oldPortalIndex); this.mission.portals.set(oldPortalIndex, a), this.mission.portals.set(options.portal, b); } main.state.save(); } async movePortal(portalID, target) { if (0 === portalID) { const missions = main.state.missions, preMission = missions.previous(this.mission); if (preMission?.portals.isEnd(target)) { if (await confirmDialog({ message: "Merge mission ?" })) return missions.merge(preMission, this.mission), void main.state.setCurrent(preMission.id); } else if (1 === this.mission.portals.length && preMission?.portals.includes(target.guid) && await confirmDialog({ message: "Split mission ?" })) { const index = preMission.portals.indexOf(target.guid); return this.mission.portals.clear(), void missions.split(preMission, index, this.mission); } } if (portalID === this.mission.portals.length - 1) { const missions = main.state.missions, postMission = missions.next(this.mission); if (postMission?.portals.isStart(target) && await confirmDialog({ message: "Merge mission ?" })) return void missions.merge(this.mission, postMission); } this.mission.portals.set(portalID, target); } getSnapPortal(unsnappedLatLng) { const containerPoint = window.map.latLngToContainerPoint(unsnappedLatLng); let best_portal, best_distance = 1 / 0; for (const guid in window.portals) { const portal = window.portals[guid], ll = portal.getLatLng(), pp = window.map.latLngToContainerPoint(ll), options = portal.options, size = options.weight + 5 * options.radius, distance = pp.distanceTo(containerPoint); distance > size || distance < best_distance && (best_distance = distance, best_portal = portal); } return best_portal; } onMarkerDblClick(event) { const options = event.target.options, portal = options.portal; options.isMidPoint || (this.mission.portals.remove(portal), main.state.save(), notification(`${this.mission.title}\nRemoved #${portal + 1} from mission`)); } } class RenderBase { layer; constructor() { this.layer = new window.L.FeatureGroup, this.toggle(!1); } isVisible() { return window.map.hasLayer(this.layer); } isLayer(layer) { return layer === this.layer; } toggle(show) { show ? window.map.addLayer(this.layer) : window.map.removeLayer(this.layer); } } class RenderPath extends RenderBase { dragMarkers; constructor() { super(), this.dragMarkers = [], main.state.onMissionChange.do(this.redraw), main.state.onMissionPortal.do(this.redraw), main.state.onSelectedMissionChange.do(this.redraw); } redrawNow=() => { this.layer.clearLayers(), this.dragMarkers.forEach(m => m.destroy()), this.dragMarkers = []; const editMode = main.missionModeActive; main.state.missions.forEach(mission => { main.state.isCurrent(mission.id) && editMode ? this.drawEditMission(mission) : this.drawMission(mission); }); }; redraw=debounce(this.redrawNow); drawMission(mission) { const geodesicPolyline = new L.GeodesicPolyline(mission.getLocations(), { color: main.state.isCurrent(mission.id) ? "#ff9a00" : "crimson", weight: 5, smoothFactor: 1, interactive: !1 }); this.layer.addLayer(geodesicPolyline); } drawEditMission(mission) { const coordinatesList = mission.getLocations(); coordinatesList.forEach((ll, index) => this.createDragMarker(ll, index, mission)), coordinatesList.forEach((ll, index) => { if (index > 0) { const half = this.getCenter(coordinatesList[index - 1], ll); this.createDragMarker(half, index, mission, !0); } }); const geodesicPolyline = new L.GeodesicPolyline(coordinatesList, { color: "#ff9a00", weight: 5, smoothFactor: 1 }); this.layer.addLayer(geodesicPolyline); } createDragMarker(location, portalId, mission, dummy = !1) { this.dragMarkers.push(new DragMarker(this.layer, location, portalId, mission, dummy)); } getCenter(l1, l2) { const p1 = window.map.project(l1), p2 = window.map.project(l2); return window.map.unproject(p1.add(p2).divideBy(2)); } } class RenderNumbers extends RenderBase { constructor() { super(), main.state.onMissionChange.do(this.redraw), main.state.onMissionPortal.do(this.redraw), main.state.onSelectedMissionChange.do(this.redraw); } redrawNow=() => { this.layer.clearLayers(); const state = main.state; this.getMissionStarts(state).forEach(start => { const id = start.missionIndex, icon = this.generateMarker(state.isCurrent(id) ? "active" : "start", id + 1), marker = L.marker(start.location, { icon: L.divIcon({ className: "umm-mission-icon", iconSize: [ 34, 50 ], iconAnchor: [ 17, 50 ], html: icon }), opacity: start.auto ? .4 : 1, interactive: !1 }); this.layer.addLayer(marker); }); }; redraw=debounce(this.redrawNow); getMissionStarts(state) { const missions = []; let mid = 0; for (;mid < state.missions.count(); ) { const mission = state.missions.get(mid); if (mission?.hasPortals()) { const start = mission.portals.getLatLngOf(0); missions.push({ missionIndex: mid, location: start, auto: !1 }); } let count = 1; for (;mid + count < state.missions.count(); count++) { const nextMission = state.missions.get(mid + count); if (nextMission?.hasPortals()) break; } if (count > 1) { const allLocations = []; for (let i = 0; i < count - 1; i++) { const fillMission = state.missions.get(mid + i); fillMission?.hasPortals() && allLocations.push(...fillMission.getLocations()); } const portalsPerMission = Math.max(allLocations.length / count, 6); for (let fillIndex = 1; fillIndex < count; fillIndex++) { const locationIndex = Math.floor(portalsPerMission * fillIndex); locationIndex < allLocations.length - 1 && missions.push({ missionIndex: mid + fillIndex, location: allLocations[locationIndex], auto: !0 }); } } mid += count; } return missions; } generateMarker(kclass, index) { return `${index}`; } } class Portals { state; data; constructor(state, data) { this.state = state, this.data = data; } cloneWithoutEvents() { return new Portals(void 0, [ ...this.data ]); } get length() { return this.data.length; } get(index) { return this.data.at(index); } getRange(start, end) { return this.data.slice(start, end); } set(index, portal) { this.data[index] = portal, this.state?.onMissionPortal.trigger(); } add(...portal) { portal.some(p => this.includes(p.guid)), this.data.push(...portal), this.state?.onMissionPortal.trigger(); } insert(index, ...portal) { portal.some(p => this.includes(p.guid)), this.data.splice(index, 0, ...portal), this.state?.onMissionPortal.trigger(); } remove(index, count = 1) { this.data.splice(index, count), this.state?.onMissionPortal.trigger(); } clear() { this.data.length = 0, this.state?.onMissionPortal.trigger(); } toLatLng() { return this.data.map(portal => new L.LatLng(portal.location.latitude, portal.location.longitude)); } getLatLngOf(index) { const portal = this.get(index); if (portal) return new L.LatLng(portal.location.latitude, portal.location.longitude); } includes(guid) { return this.data.some(x => x.guid === guid); } find(guid) { return this.data.find(x => x.guid === guid); } indexOf(guid) { return this.data.findIndex(x => x.guid === guid); } isStart(portal) { return this.data[0]?.guid === portal.guid; } isEnd(portal) { return this.data.at(-1)?.guid === portal.guid; } reverse() { this.data.reverse(), this.state?.onMissionPortal.trigger(); } create(guid) { const iitcPortal = window.portals[guid], options = iitcPortal.options.data, ll = iitcPortal.getLatLng(); return { guid, title: options.title || "[undefined]", imageUrl: options.image, description: "", location: { latitude: ll.lat, longitude: ll.lng }, isOrnamented: !1, isStartPoint: !1, type: "PORTAL", objective: { type: "HACK_PORTAL", passphrase_params: { question: "", _single_passphrase: "" } } }; } getDistance() { return this.toLatLng().reduce((sum, ll, index, lls) => index > 0 ? sum + ll.distanceTo(lls[index - 1]) : 0, 0); } } const setPassphrase = (portal, question, answer) => { portal.objective.passphrase_params.question = question, portal.objective.passphrase_params._single_passphrase = answer; }; class Mission { missionID; data; portal_data; constructor(state, id, data) { this.missionID = id, this.data = data, this.portal_data = new Portals(state, data.portals); } get title() { return this.data.missionTitle; } get portals() { return this.portal_data; } get id() { return this.missionID; } get description() { return this.data.missionDescription; } hasPortals() { return this.portal_data.length > 0; } getLocations() { return this.portal_data.toLatLng(); } show() { if (this.hasPortals()) { const bounds = new L.LatLngBounds(this.getLocations()).pad(.2); window.map.fitBounds(bounds, { maxZoom: 18 }); } } focusLastPortal() { const last_ll = this.portal_data.getLatLngOf(-1), last = this.portal_data.get(-1); return !(!last || !last_ll) && (window.map.setView(last_ll), window.renderPortalDetails(last.guid), !0); } getDistance() { return this.portals.getDistance(); } clear() { this.portal_data.clear(); } reverse() { this.portal_data.reverse(); } } class Missions { static generateMissionTitle(format, info) { return format.replace(/\$(\d*)?(\w)/g, (_, flags, token) => { let value = token; switch (token.toLowerCase()) { case "t": value = info.title ?? value; break; case "m": value = info.total?.toString() ?? value; break; case "n": value = ((info.misison || 0) + 1).toString() ?? value; } let leadingZero = !1; flags?.startsWith("0") && (leadingZero = !0, flags = flags.slice(1)); let length = parseInt(flags); return Number.isNaN(length) && (length = 1, leadingZero && (length = info.total?.toString().length ?? 1)), value.length < length && (value = value.padStart(length, leadingZero ? "0" : " ")), value; }); } state; data; constructor(state, data) { this.state = state, this.data = data; } get(missionId) { const mis = this.data[missionId]; return mis && new Mission(this.state, missionId, mis); } count() { return this.data.length; } forEach(callback) { this.data.forEach((missionData, index) => { const mission = new Mission(this.state, index, missionData); callback(mission); }); } filter(callback) { const result = []; return this.forEach(mission => { callback(mission) && result.push(mission); }), result; } previous(mission) { let preMission, preMissionID = mission.id - 1; for (;!(preMission = this.get(preMissionID))?.hasPortals() && preMissionID > 0; ) preMissionID--; return preMission; } next(mission) { return this.get(mission.id + 1); } distanceToStart(id) { const mission = this.get(id); if (!mission) return; const previous = this.previous(mission), first = previous?.portals.getLatLngOf(-1), last = mission.portals.getLatLngOf(0); return first && last ? first.distanceTo(last) : void 0; } getTotalDistance() { const waypoints = []; return this.forEach(m => waypoints.push(...m.getLocations())), waypoints.reduce((sum, ll, index, lls) => index > 0 ? sum + ll.distanceTo(lls[index - 1]) : 0, 0); } getWaypointCount() { return this.data.reduce((count, mis) => count + mis.portals.length, 0); } validate() { const errors = {}, notEnoughWaypoint = this.filter(m => m.portals.length < 6).map(m => m.id); return notEnoughWaypoint.length > 0 && (errors["not enough waypoints"] = notEnoughWaypoint), errors; } zoom() { const location = this.data.flatMap(m => new Portals(this.state, m.portals).toLatLng()); if (location.length > 0) { const bounds = new L.LatLngBounds(location).pad(.1); window.map.fitBounds(bounds, { maxZoom: 18 }); } } merge(destination, missionB) { destination.portals.add(...missionB.portals.getRange()), missionB.portals.clear(); } mergeAll() { const portals = []; this.data.forEach(m => { portals.push(...m.portals), m.portals.length = 0; }), this.data[0].portals = portals; } split(source, at, destination) { const toMove = source.portals.getRange(at); destination.portals.insert(0, ...toMove), source.portals.remove(at, toMove.length); } splitIntoMultiple(source, count, restAtLast = !1) { const allPortals = this.getAllPortalsOf(source.id, count); let portalsPerMission = allPortals.length / count; restAtLast && (portalsPerMission = Math.floor(portalsPerMission)); for (let i = 0; i < count; i++) { const start = Math.floor(portalsPerMission * i); let end = Math.floor(portalsPerMission * (i + 1)); i === count - 1 && (end = allPortals.length); const mission = this.get(source.id + i); mission?.portals.clear(), mission?.portals.add(...allPortals.slice(start, end)); } } getAllPortalsOf(from, count) { const allPortals = []; for (let i = 0; i < count; i++) { const mission = this.get(from + i); mission && allPortals.push(...mission.portals.getRange()); } return allPortals; } getMissionsOfPortal(guid) { return this.filter(mis => mis.portals.includes(guid)).map(m => m.id); } reverse(from, to) { if (to) { (from = Math.min(Math.max(from, 0), this.count() - 1)) > (to = Math.min(Math.max(to, 0), this.count() - 1)) && ([from, to] = [ to, from ]); const portal_copy = this.data.map(mission => mission.portals.splice(0)); for (let i = from; i <= to; i++) this.data[i].portals = portal_copy[to - (i - from)].reverse(); } else this.get(from)?.reverse(); } } const undefinedOrEmptyString = value => null == value || "" == value; class Trigger { handler=[]; do(fct) { this.handler.includes(fct) || this.handler.push(fct); } dont(fct) { const index = this.handler.indexOf(fct); -1 === index ? console.error("handler was not registerd", fct) : this.handler.splice(index, 1); } trigger() { this.handler.some(fct => !1 === fct()); } clear() { this.handler = []; } } class State { theState; constructor() { this.load(); } onSelectedMissionChange=new Trigger; onMissionChange=new Trigger; onMissionPortal=new Trigger; load() { this.reset(); const data = localStorage.getItem("ultimate-mission-maker"); data && this.import(data); } save() { this.setPlannedLength(this.theState.plannedBannerLength), localStorage.setItem("ultimate-mission-maker", this.asString()); } import(jsonString) { const anyState = JSON.parse(jsonString); this.theState = (ummState => { if (ummState.fileFormatVersion > 3) throw new Error("UMM: You've attempted to load data that's newer than what's supported by this version of UMM. Please update the plugin and try again. Data has not been loaded."); if (void 0 === ummState.fileFormatVersion || "" === ummState.fileFormatVersion) { if (undefinedOrEmptyString(ummState.missionSetName) && (undefinedOrEmptyString(ummState.missionName) ? ummState.missionSetName = "" : (ummState.missionSetName = ummState.missionName, delete ummState.missionName)), undefinedOrEmptyString(ummState.missionSetDescription) && (undefinedOrEmptyString(ummState.missionDescription) ? ummState.missionSetDescription = "" : (ummState.missionSetDescription = ummState.missionDescription, delete ummState.missionDescription)), undefinedOrEmptyString(ummState.titleFormat) && (ummState.titleFormat = "T NN-M"), void 0 === ummState.numberOfMissions ? ummState.plannedBannerLength = Object.keys(ummState.missions).length : (ummState.plannedBannerLength = ummState.numberOfMissions, delete ummState.numberOfMissions), !Object.keys(ummState.missions[0]).includes("portals")) if (ummState.missions[0][0].guid) { const newMissions = []; for (const mission in ummState.missions) { const plannedLength = ummState.plannedBannerLength > 0 ? ummState.plannedBannerLength : ummState.missions.length, missionTitle = Missions.generateMissionTitle(ummState.titleFormat, { misison: parseInt(mission) + 1, title: ummState.missionSetName, total: plannedLength }); newMissions.push({ missionTitle, missionDescription: ummState.missionSetDescription, portals: ummState.missions[mission] }); } ummState.missions = newMissions; } else ummState.missions = [ { missionTitle: "", missionDescription: "", portals: [] } ]; ummState.fileFormatVersion = 1; } if (1 === ummState.fileFormatVersion) { for (const mission in ummState.missions) for (const portal in ummState.missions[mission].portals) ummState.missions[mission].portals[portal].objective = { type: "HACK_PORTAL", passphrase_params: { question: "", _single_passphrase: "" } }; ummState.fileFormatVersion = 2; } if (2 === ummState.fileFormatVersion) for (const mission in ummState.missions) for (const portal in ummState.missions[mission].portals) "HACK" === ummState.missions[mission].portals[portal].objective.type && (ummState.missions[mission].portals[portal].objective.type = "HACK_PORTAL"); return 2 === ummState.fileFormatVersion && (ummState.missionSetName ??= "", ummState.missionSetDescription ??= "", ummState.currentMission ??= 0, ummState.plannedBannerLength ??= 1, ummState.titleFormat ??= "T NN-M"), ummState.fileFormatVersion < 3 && (ummState.titleFormat = (ummState.titleFormat ?? "").replace("T", "$T").replace(/N+/, match => match.length > 1 ? "$0N" : "$N").replace(/(M+)/g, "$M"), ummState.fileFormatVersion = 3), ummState; })(anyState), this.setPlannedLength(this.getPlannedLength() || 1), this.onMissionChange.trigger(), this.onMissionPortal.trigger(), this.onSelectedMissionChange.trigger(); } asString() { return JSON.stringify(this.theState); } reset() { this.theState = { missionSetName: "", missionSetDescription: "", currentMission: 0, plannedBannerLength: 1, titleFormat: "$T $N / $M", fileFormatVersion: 3, missions: [ { missionTitle: "", missionDescription: "", portals: [] } ], layers: [] }, this.onMissionChange.trigger(); } isEmpty() { return "" === this.theState.missionSetName && "" === this.theState.missionSetDescription && this.theState.missions.every(m => 0 === m.portals.length); } isValid() { return "" !== this.theState.missionSetName && "" !== this.theState.missionSetDescription && this.theState.plannedBannerLength > 0; } get missions() { return new Missions(this, this.theState.missions); } getBannerName() { return this.theState.missionSetName; } setBannerName(name) { this.theState.missionSetName = name, this.theState.missions.forEach((mission, id) => mission.missionTitle = this.generateMissionTitle(id)), this.onMissionChange.trigger(); } getBannerDesc() { return this.theState.missionSetDescription; } setBannerDesc(desc) { this.theState.missionSetDescription = desc, this.theState.missions.forEach(mission => mission.missionDescription = this.theState.missionSetDescription), this.onMissionChange.trigger(); } getTitleFormat() { return this.theState.titleFormat; } setTitleFormat(name) { this.theState.titleFormat = name, this.theState.missions.forEach((mission, id) => mission.missionTitle = this.generateMissionTitle(id)), this.onMissionChange.trigger(); } getPlannedLength() { return this.theState.plannedBannerLength; } setPlannedLength(count) { if (count = Math.max(count, 1), this.theState.plannedBannerLength = count, this.theState.missions.length > count) this.theState.missions = this.theState.missions.slice(0, count); else for (let id = this.theState.missions.length; id < count; id++) this.theState.missions.push({ missionTitle: this.generateMissionTitle(id), missionDescription: this.theState.missionSetDescription, portals: [] }); this.onMissionChange.trigger(); } generateMissionTitle(missNumber) { return Missions.generateMissionTitle(this.theState.titleFormat, { misison: missNumber, total: this.getPlannedLength(), title: this.theState.missionSetName }); } getEditMission() { return this.missions.get(this.theState.currentMission); } setCurrent(missionId) { missionId >= 0 && this.getPlannedLength(), this.theState.currentMission = missionId, this.onSelectedMissionChange.trigger(); } getCurrent() { return this.theState.currentMission; } isCurrent(missionId) { return this.theState.currentMission === missionId; } checkPortal(event) { let updated = !1; this.theState.missions.forEach(mission => { const portal = mission.portals.find(x => x.guid === event.guid); portal && (portal.imageUrl === event.portalData.image && portal.title === event.portalData.title || (portal.imageUrl = event.portalData.image, portal.title = event.portalData.title, updated = !0)); }), updated && this.save(); } checkAllPortals() { let updated = !1; this.theState.missions.forEach(mission => { mission.portals.forEach(portal => { const iitcPortal = window.portals[portal.guid]?.options.data; iitcPortal && (portal.imageUrl === iitcPortal.image && portal.title === iitcPortal.title || (portal.imageUrl = iitcPortal.image, portal.title = iitcPortal.title, updated = !0)); }); }), updated && this.save(); } storeLayerState(layers) { this.theState.layers = layers.map(l => l.isVisible()), this.save(); } restoreLayerState(layers) { this.theState.layers.forEach((vis, index) => layers[index].toggle(vis ?? !0)); } } const Button_button = (label, click, classes) => $("